基于spring-ldap-core-1.3.1
AndFilter filter = new AndFilter();
filter.and(new GreaterThanOrEqualsFilter("filterCondition", "your filterCondition"));
String baseNam = "";//这里可以指定为""或者你要查询的某个根目录, 如:cn=testdata
List<TestData> list = new ArrayList<TestData>();
//声明搜索控件
SearchControls schCtrls = new SearchControls();
// 返回属性设置
String[] returnAttrs = { "testName", "testTime", "testUser"};
schCtrls.setReturningAttributes(returnAttrs);
//指定检索范围
/*
* 0:OBJECT_SCOPE,搜索指定的命名对象。
* 1:ONELEVEL_SCOPE,只搜索指定命名对象的一个级别,这是缺省值。
* 2:SUBTREE_SCOPE,搜索以指定命名对象为根结点的整棵树 **/
schCtrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
int pageSize = 500;
byte[] cookie = null;
ContextSource contextSource = ldapTemplate.getContextSource();
DirContext ctx = contextSource.getReadWriteContext();
LdapContext lCtx = (LdapContext) ctx;
//分页
lCtx.setRequestControls(new Control[] { new PagedResultsControl( pageSize, Control.CRITICAL) });
int totalResults = 0;
do {
//搜索的结果的枚举属性
NamingEnumeration<SearchResult> results = lCtx.search(baseNam, filter.toString(), schCtrls);
while (null != results && results.hasMoreElements()) {//结果不为空且有值
SearchResult sr = results.next();
Attributes attrs = sr.getAttributes();
TestData testData = dataConvert(attrs);
list.add(testData);
totalResults++;
}
//cookie是一个字节数组,包含了通过PagedResultsControl下一次调用服务器时所需的信息
cookie = parseControls(lCtx.getResponseControls());
lCtx.setRequestControls(new Control[] { new PagedResultsControl( pageSize, cookie, Control.CRITICAL) });
} while ((cookie != null) && (cookie.length != 0));
lCtx.close();
System.out.println("Total = " + totalResults);
return list;
}
/**
* 将LDAP的操作日志changlog转换成TestData对象
* @param attrs
* @return
*/
private TestData dataConvert(Attributes attrs) {
TestData testData = new TestData();
Map<String, String> testDataMap = new HashMap<String, String>();// 查找TestData对象每个成员变量的类型,用于后续的数据转换
Class objClass = testData.getClass();
for (int i = 0; i < objClass.getDeclaredFields().length; i++)
{
Field field = objClass.getDeclaredFields()[i];
if (field.getType().toString().endsWith("List"))
testDataMap.put(field.getName(), "List");
else
testDataMap.put(field.getName(), "String");
}
testDataMap.put("reqMod", "List");
NamingEnumeration results = attrs.getAll(); // 检索属性集中属性的枚举
try
{
while (results.hasMoreElements())
{
Attribute attr = null;
try
{
attr = (Attribute) results.next(); // 检索枚举中的下一个元素
} catch (NamingException e)
{
e.printStackTrace();
}
String attrValue = null;
try
{
attrValue = attr.get().toString(); // 检索此属性的其中一个值
} catch (NamingException e)
{
e.printStackTrace();
}
if (attrValue != null && attrValue.length() > 0)
{
String attrName = attr.getID(); // 检索此属性的 id
List<String> list = new ArrayList<String>();
NamingEnumeration map = attr.getAll(); // 用于判断属性值是否为多值属性
if (map != null)
{
while (map.hasMore())
{
String mod = map.next().toString();
list.add(mod);
}
}
if ("List".equals(testDataMap.get(attrName)))
BeanUtils.setProperty(testData, attrName, list);// 多值成员变量处理方式
else
BeanUtils.setProperty(testData, attrName, attrValue);// 单值成员变量处理方式
}
}
} catch (Exception ex)
{
ex.printStackTrace();
}
return testData;
}
//下次查询要用的cookie
private static byte[] parseControls(Control[] controls)
throws NamingException {
byte[] cookie = null;
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];
cookie = prrc.getCookie();
System.out.println(">>Next Page \n");
}
}
}
return (cookie == null) ? new byte[0] : cookie;
}
spring ldap配置:
<bean id="contextSource" class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
<constructor-arg ref="pooledContextSource"/>
</bean>
<bean id="pooledContextSource" class="org.springframework.ldap.pool.factory.MutablePoolingContextSource">
<property name="contextSource" ref="contextSourceTarget"/>
<property name="dirContextValidator" ref="dirContextValidator"/>
<property name="testOnBorrow" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="minIdle" value="0"/>
<property name="maxIdle" value="8"/>
<property name="maxActive" value="8"/>
<property name="maxTotal" value="100"/>
<property name="maxWait" value="-1"/>
</bean>
<bean id="dirContextValidator" class="org.springframework.ldap.pool.validation.DefaultDirContextValidator"/>
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://127.0.01:389" />
<property name="userDn" value="cn=root,dc=testdata" />
<property name="password" value="12345"/>
<property name="pooled" value="false" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
<!-- 将ldapTemplate注入到testDataDao-->
<bean id="testDataDao_LDAP" class="com.talkweb.mainSyn.dal.impl.TestDataDaoImpl">
<property name="ldapTemplate">
<ref bean="ldapTemplate" />
</property>
</bean>
参考资料: http://docs.spring.io/spring-ldap/docs/1.3.2.RELEASE/reference/html/pooling.html
http://blog.csdn.net/xufaxi/article/details/5840691
http://docs.spring.io/spring-ldap/docs/1.3.2.RELEASE/reference/html/index.html
-----------------------------------------------------------------------------------------------------------------------
9. Pooling Support
Pooling LDAP connections helps mitigate the overhead ofcreating a new LDAP connection for each LDAP interaction.WhileJava LDAP pooling supportexists it is limited in its configuration options andfeatures, such as connection validation and poolmaintenance. Spring LDAP provides support for detailed poolconfiguration on a per-ContextSource
basis.
Pooling support is provided byPoolingContextSource
which can wrap anyContextSource
and pool both read-only and read-writeDirContext
objects.Jakarta Commons-Poolis used to provide the underlying pool implementation.
Validation of pooled connections is the primary motivationfor using a custom pooling library versus the JDK providedLDAP pooling functionality. Validation allows pooledDirContext
connections to be checked to ensure they are still properlyconnected and configured when checking them out of the pool,in to the pool or while idle in the pool
TheDirContextValidator
interface is used by thePoolingContextSource
for validation andDefaultDirContextValidator
is provided as the default validation implementation.DefaultDirContextValidator
does aDirContext.search(String, String, SearchControls)
, with an empty name, a filter of"objectclass=*"
andSearchControls
set to limit a single result with the only the objectclassattribute and a 500ms timeout. If the returnedNamingEnumeration
has results theDirContext
passes validation, if no results are returned or anexception is thrown theDirContext
fails validation. TheDefaultDirContextValidator
should work with no configuration changes on most LDAPservers and provide the fastest way to validate theDirContext
.
The following properties are available on thePoolingContextSource
for configuration of the DirContext pool. ThecontextSource
property must be set and thedirContextValidator
property must be set if validation is enabled, all otherproperties are optional.
Table 9.1. Pooling Configuration Properties
Parameter | Default | Description |
---|---|---|
contextSource | null | TheContextSource implementation to getDirContext s from to populate the pool. |
dirContextValidator | null | TheDirContextValidator implementation to use when validatingconnections. This is required iftestOnBorrow ,testOnReturn , ortestWhileIdle options are set totrue . |
maxActive | 8 | The maximum number of active connections ofeach type (read-only|read-write) that can beallocated from this pool at the same time,or non-positive for no limit. |
maxTotal | -1 | The overall maximum number of activeconnections (for all types) that can beallocated from this pool at the same time,or non-positive for no limit. |
maxIdle | 8 | The maximum number of active connections ofeach type (read-only|read-write) that canremain idle in the pool, without extra onesbeing released, or non-positive for nolimit. |
minIdle | 0 | The minimum number of active connections ofeach type (read-only|read-write) that canremain idle in the pool, without extra onesbeing created, or zero to create none. |
maxWait | -1 | The maximum number of milliseconds that thepool will wait (when there are no availableconnections) for a connection to be returnedbefore throwing an exception, ornon-positive to wait indefinitely. |
whenExhaustedAction | 1 (BLOCK) | Specifies the behaviour when the pool isexhausted.
|
testOnBorrow | false | The indication of whether objects will bevalidated before being borrowed from thepool. If the object fails to validate, itwill be dropped from the pool, and anattempt to borrow another will be made. |
testOnReturn | false | The indication of whether objects will bevalidated before being returned to the pool. |
testWhileIdle | false | The indication of whether objects will bevalidated by the idle object evictor (ifany). If an object fails to validate, itwill be dropped from the pool. |
timeBetweenEvictionRunsMillis | -1 | The number of milliseconds to sleep betweenruns of the idle object evictor thread. Whennon-positive, no idle object evictor threadwill be run. |
numTestsPerEvictionRun | 3 | The number of objects to examine during eachrun of the idle object evictor thread (ifany). |
minEvictableIdleTimeMillis | 1000 * 60 * 30 | The minimum amount of time an object may sitidle in the pool before it is eligible foreviction by the idle object evictor (ifany). |
Configuring pooling should look very familiar if you're usedto Jakarta Commons-Pool or Commons-DBCP. You will firstcreate a normalContextSource
then wrap it in aPoolingContextSource
.
<beans> ... <bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource"> <property name="contextSource" ref="contextSourceTarget" /> </bean> <bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource"> <property name="url" value="ldap://localhost:389" /> <property name="base" value="dc=example,dc=com" /> <property name="userDn" value="cn=Manager" /> <property name="password" value="secret" /> <property name="pooled" value="false"/> </bean> ... </beans>
In a real world example you would probably configure thepool options and enable connection validation; the aboveserves as an example to demonstrate the general idea.
Note | |
---|---|
Ensure that the |
Note | |
---|---|
You'll notice that the actual |
Adding validation and a few pool configuration tweaks tothe above example is straight forward. Inject aDirContextValidator
and set when validation should occur and the pool isready to go.
<beans> ... <bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource"> <property name="contextSource" ref="contextSourceTarget" /> <property name="dirContextValidator" ref="dirContextValidator" /> <property name="testOnBorrow" value="true" /> <property name="testWhileIdle" value="true" /> </bean> <bean id="dirContextValidator" class="org.springframework.ldap.pool.validation.DefaultDirContextValidator" /> <bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource"> <property name="url" value="ldap://localhost:389" /> <property name="base" value="dc=example,dc=com" /> <property name="userDn" value="cn=Manager" /> <property name="password" value="secret" /> <property name="pooled" value="false"/> </bean> ... </beans>
The above example will test eachDirContext
before it is passed to the client application and testDirContext
s that have been sitting idle in the pool.
The PoolingContextSource
assumes that all DirContext
objects retrieved from ContextSource.getReadOnlyContext()
will have the same environment and likewise that allDirContext
objects retrieved from ContextSource.getReadWriteContext()
will have the same environment. This means that wrapping aLdapContextSource
configured with an AuthenticationSource
in a PoolingContextSource
will not function as expected. The pool would be populated using the credentials of the first user and unless new connections were needed subsequent context requests would not be filled for the user specified by the AuthenticationSource
for the requesting thread.