很多文章中都有相关分页操作,以下是我使用分页操作时出现的问题,以及如何解决。
使用VirtualListViewControl分页时,还存在点问题(就是查询第一页和第二页的时候,有几条数据是重复的,希望有高手可以指点下。)
源码在附件中有。附件中是好几种分页的实现。但都不是很完美。希望有高手分享下。
package com.zhao.edu;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.SortControl;
public class LdapSearch {
private static final String ldapHost = "ldap://192.168.32.128:389";
private static final String loginDN = "cn=Administrator,cn=Users,dc=zhao,dc=edu,dc=com";
private static final String password = "zhaowg";
public static void main(String[] args) throws Exception{
InitialLdapContext ctx = LdapConnection.getLdapContext(ldapHost,loginDN,password);
try {
int pageNum = 30;
int pageSize = 500;
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setCountLimit(0);
constraints.setTimeLimit(5000);
constraints.setDerefLinkFlag(false);
String[] attrIDs = { "sAMAccountName" ,"distingguishedName"};
constraints.setReturningAttributes(attrIDs);
VirtualListViewControl vctl = new VirtualListViewControl(
(pageNum-1)*pageSize+1, 0, 0,pageSize-1, Control.CRITICAL);
SortControl sctl = new SortControl(
new String[] { "sAMAccountName" },Control.CRITICAL);
ctx.setRequestControls(new Control[] { sctl, vctl });
String baseDN="dc=zhao,dc=edu,dc=com";
String filter="objectClass=*";
NamingEnumeration<SearchResult> results = ctx.search(
baseDN,filter, constraints);
int total = 0;
while (results != null && results.hasMore()) {
total++;
SearchResult result = results.nextElement();
String value = result.getName();
//System.out.print(total + " 节点DN: " + value);
Attributes attrs = result.getAttributes();
Attribute ocAttr= attrs.get("sAMAccountName");
for(int i=0;ocAttr!=null && i<ocAttr.size();i++){
System.out.print(" " + ocAttr.get(i) +"\r\n");
}
}
System.out.println("查询条数:" + total);
} catch (Exception e) {
e.printStackTrace();
} finally {
LdapConnection.closeLdapContext(ctx);
}
}
}
package com.zhao.edu;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.InitialLdapContext;
public class LdapConnection {
/* 服务提供者 */
private static String CTX_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
/**
* 获取上下文
* @param url
* @param username
* @param pwd
* @return
*/
public static InitialLdapContext getLdapContext(String url, String username,
String pwd) {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, CTX_FACTORY);
env.put(Context.PROVIDER_URL, url);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, pwd);
DirContext ctx = null;
try {
ctx = new InitialLdapContext(env, null);
System.out.println("认证成功");
} catch (NamingException e) {
e.printStackTrace();
System.out.println("认证失败");
}
return (InitialLdapContext) ctx;
}
/**
* 关闭上下文
* @param ctx
* @throws NamingException
*/
public static void closeLdapContext(DirContext ctx) throws NamingException
{
if(null!=ctx)
{
ctx.close();
System.out.println("关闭连接");
}
}
}
package com.zhao.edu;
import java.io.IOException;
import javax.naming.directory.InvalidAttributeValueException;
import javax.naming.ldap.*;
import com.sun.jndi.ldap.BasicControl;
import com.sun.jndi.ldap.Ber;
import com.sun.jndi.ldap.BerEncoder;
/**
* This class implements the LDAPv3 Request Control for virtual-list-view
* as defined in
* <a href="http://www.ietf.org/internet-drafts/draft-ietf-ldapext-ldapv3-vlv-09.txt">draft-ietf-ldapext-ldapv3-vlv-09.txt</a>.
*
* The control's value has the following ASN.1 definition:
* <pre>
*
* VirtualListViewRequest ::= SEQUENCE {
* beforeCount INTEGER (0 .. maxInt),
* afterCount INTEGER (0 .. maxInt),
* CHOICE {
* byoffset [0] SEQUENCE {
* offset INTEGER (0 .. maxInt),
* contentCount INTEGER (0 .. maxInt)
* }
* greaterThanOrEqual [1] AssertionValue
* }
* contextID OCTET STRING OPTIONAL
* }
*
* </pre>
*
* This control is always used in conjunction with the server-side sort control
* (<a href="http://www.ietf.org/rfc/rfc2891.txt">RFC-2891</a>).
*
* @see VirtualListViewResponseControl
* @see SortControl
* @author Vincent Ryan
*/
final public class VirtualListViewControl extends BasicControl {
/**
* The virtual-list-view control's assigned object identifier
* is 2.16.840.1.113730.3.4.9.
*/
public static final String OID = "2.16.840.1.113730.3.4.9";
/**
* The number of entries before the target entry in a sublist.
*
* @serial
*/
private int beforeCount;
/**
* The number of entries after the target entry in a sublist.
*
* @serial
*/
private int afterCount;
/**
* An offset into the list.
*
* @serial
*/
private int targetOffset = -1;
/**
* An estimate of the number of entries in the list.
*
* @serial
*/
private int listSize;
/**
* Attribute value used to locate the target entry.
* This value is compared to values of the attribute specified
* as the primary sort key.
*
* Only String and byte[] values are currently supported.
*
* @serial
*/
private Object targetAttrValue = null;
/**
* A server-generated cookie.
*
* @serial
*/
private byte[] cookie = null;
private static final long serialVersionUID = 7739016048653396131L;
/**
* Constructs a virtual-list-view control.
*
* Request a view of a portion of the list centered around a given
* target entry. The position of the target entry is estimated as a
* percentage of the list.
*
* @param targetPercentage The position of the target entry expressed as a
* percentage of the list. For example, a value of
* 25 indicates that the target entry is at the
* 25 percent mark in the list.
* @param viewSize The number of entries to be returned in this
* view of the list.
* @param criticality The control's criticality setting.
* @exception IllegalArgumentException if targetPercentage is
* outside the range 0-100.
* @exception IOException If a BER encoding error occurs.
*/
public VirtualListViewControl(int targetPercentage, int viewSize,
boolean criticality) throws IOException {
super(OID, criticality, null);
if ((targetPercentage > 100) ||
(targetPercentage < 0) ||
(viewSize < 0)) {
throw new IllegalArgumentException();
}
targetOffset = targetPercentage;
listSize = 100;
// viewSize includes the target entry
if (viewSize > 0) {
viewSize -= 1;
}
beforeCount = afterCount = viewSize / 2;
// adjust afterCount when viewSize is odd
if (viewSize != ((viewSize / 2) * 2)) {
afterCount++;
}
super.value = setEncodedValue();
}
/**
* Constructs a virtual-list-view control.
*
* Request a view of a portion of the list with the specified number of
* entries before and after a given target entry. The target entry is
* identified by means of an offset into the list.
*
* @param targetOffset The position of the target entry as an offset
* into the list.
* @param listSize An estimate of the number of entries in the list.
* @param beforeCount The number of entries to be returned before the
* target entry.
* @param afterCount The number of entries to be returned after the
* target entry.
* @param criticality The control's criticality setting.
* @exception IllegalArgumentException if targetOffset, listSize,
* beforeCount or afterCount are less than zero.
* @exception IOException If a BER encoding error occurs.
*/
public VirtualListViewControl(int targetOffset, int listSize,
int beforeCount, int afterCount, boolean criticality)
throws IOException {
super(OID, criticality, null);
if ((targetOffset <0) || (listSize <0) ||(beforeCount <0) || (afterCount <0)){
throw new IllegalArgumentException();
}
this.targetOffset = targetOffset;
this.listSize = listSize;
this.beforeCount = beforeCount;
this.afterCount = afterCount;
super.value = setEncodedValue();
}
/**
* Constructs a virtual-list-view critical control.
*
* Request a view of a portion of the list centered around a given
* target entry. The target entry is the first entry that is greater
* than or equal to the specified attribute value. The value's
* attribute ID is the primary sort key specified in the server-side
* sort control.
*
* @param targetAttrValue An attribute value used to locate the target
* entry. Its attribute ID is that of the primary
* sort key specified in the server-side sort
* control.
* @param viewSize The number of entries to be returned in this
* view of the list.
* @param criticality The control's criticality setting.
* @exception InvalidAttributeValueException if
* targetAttrValue is neither a String nor a byte[].
* @exception IOException If a BER encoding error occurs.
*/
public VirtualListViewControl(Object targetAttrValue, int viewSize,
boolean criticality)
throws InvalidAttributeValueException, IOException {
super(OID, criticality, null);
if ((targetAttrValue == null) ||
(! ((targetAttrValue instanceof String) ||
(targetAttrValue instanceof byte[])))) {
throw new InvalidAttributeValueException();
}
this.targetAttrValue = targetAttrValue;
if (viewSize < 0) {
throw new IllegalArgumentException();
}
// viewSize includes the target entry
if (viewSize > 0) {
viewSize -= 1;
}
beforeCount = afterCount = viewSize / 2;
// adjust afterCount when viewSize is odd
if (viewSize != ((viewSize / 2) * 2)) {
afterCount++;
}
super.value = setEncodedValue();
}
/**
* Constructs a virtual-list-view control.
*
* Request a view of a portion of the list with the specified number of
* entries before and after a given target entry. The target entry is
* the first entry that is greater than or equal to the specified
* attribute value. The value's attribute ID is the primary sort key
* specified in the server-side sort control.
*
* @param targetAttrValue An attribute value used to locate the target
* entry. Its attribute ID is that of the primary
* sort key specified in the server-side sort
* control.
* @param beforeCount The number of entries to be returned before the
* target entry.
* @param afterCount The number of entries to be returned after the
* target entry.
* @param criticality The control's criticality setting.
* @exception InvalidAttributeValueException if
* targetAttrValue is neither a String nor a byte[].
* @exception IllegalArgumentException if beforeCount or
* afterCount are less than zero.
* @exception IOException If a BER encoding error occurs.
*/
public VirtualListViewControl(Object targetAttrValue, int beforeCount,
int afterCount, boolean criticality)
throws InvalidAttributeValueException, IOException {
super(OID, criticality, null);
if ((targetAttrValue == null) ||
(! ((targetAttrValue instanceof String) ||
(targetAttrValue instanceof byte[])))) {
throw new InvalidAttributeValueException();
}
if ((beforeCount <0) || (afterCount <0)) {
throw new IllegalArgumentException();
}
this.targetAttrValue = targetAttrValue;
this.beforeCount = beforeCount;
this.afterCount = afterCount;
super.value = setEncodedValue();
}
/**
* Sets a server-generated cookie in the virtual-list-view request.
*
* @param contextID A server-generated cookie.
* @exception IOException If a BER encoding error occurs.
*/
public void setContextID(byte[] contextID) throws IOException {
if (this.cookie != contextID) {
this.cookie = contextID;
super.value = setEncodedValue(); // re-encode
} else {
this.cookie = contextID;
}
}
/*
* Sets the ASN.1 BER encoded value of the virtual-list-view control.
* The result is the raw BER bytes including the tag and length of
* the control's value. It does not include the controls OID or criticality.
*
* @return A possibly null byte array representing the ASN.1 BER encoded
* value of the LDAP sort control.
* @exception IOException If a BER encoding error occurs.
*/
private byte[] setEncodedValue() throws IOException {
// build the ASN.1 encoding
BerEncoder ber = new BerEncoder(64);
ber.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
ber.encodeInt(beforeCount);
ber.encodeInt(afterCount);
if (targetOffset >= 0) {
ber.beginSeq(Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 0);
ber.encodeInt(targetOffset);
ber.encodeInt(listSize);
ber.endSeq();
} else {
if (targetAttrValue instanceof String) {
ber.encodeString((String)targetAttrValue,
(Ber.ASN_CONTEXT | 1), true);
} else { // byte[]
ber.encodeOctetString((byte[])targetAttrValue,
(Ber.ASN_CONTEXT | 1));
}
}
if (cookie != null) {
ber.encodeOctetString(cookie, Ber.ASN_OCTET_STR);
}
ber.endSeq();
return ber.getTrimmedBuf();
}
}
1、搜索结果数据量大于10W或者50W以后,使用VirtualListViewControl.java,会出现异常:
javax.naming.OperationNotSupportedException: [LDAP: error code 12 - 00002040: SvcErr: DSID-031401E7, problem 5010 (UNAVAIL_EXTENSION), data 0
]; remaining name ''
解决方案: 设置AD域服务器参数:MaxTempTableSize,默认值为10000。若搜寻结果大于10W,建议设置为15W;若搜索结果大于50w,建议设置100W;若搜索结果大于100W,建议设置200W。(但不知道是否对Ldap服务器其他性能造成影响)
2、搜索结果数据量大于10W以上以后,使用PagedResultsControl,出现问题:PageSize设置小于1000.
将pageSize设置为1000时,一直在循环获取数据,不会出现异常。若小于1000,则会出现问题。