Skip to content

Commit

Permalink
Add paged search support
Browse files Browse the repository at this point in the history
Current controls do now support the paged search as specified in the
RFC2696 [1].

A new control is introduced to allow paged search. This is used in the
requests and responses allowing to specify the page size and the cookie
for the following search.

1. https://www.ietf.org/rfc/rfc2696.txt
  • Loading branch information
fmarco76 committed Nov 20, 2023
1 parent 678fc24 commit c4f26fc
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 0 deletions.
3 changes: 3 additions & 0 deletions java-sdk/ldapjdk/src/main/java/netscape/ldap/LDAPControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import netscape.ldap.ber.stream.BERTag;
import netscape.ldap.client.JDAPBERTagDecoder;
import netscape.ldap.controls.LDAPEntryChangeControl;
import netscape.ldap.controls.LDAPPagedResultsControl;
import netscape.ldap.controls.LDAPPasswordExpiredControl;
import netscape.ldap.controls.LDAPPasswordExpiringControl;
import netscape.ldap.controls.LDAPSortControl;
Expand Down Expand Up @@ -534,6 +535,8 @@ public String toString() {
LDAPSortControl.class );
LDAPControl.register( LDAPVirtualListResponse.VIRTUALLISTRESPONSE,
LDAPVirtualListResponse.class );
LDAPControl.register( LDAPPagedResultsControl.PAGEDSEARCH,
LDAPPagedResultsControl.class );
} catch (LDAPException e) {
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package netscape.ldap.controls;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import netscape.ldap.LDAPControl;
import netscape.ldap.LDAPException;
import netscape.ldap.ber.stream.BERElement;
import netscape.ldap.ber.stream.BERInteger;
import netscape.ldap.ber.stream.BEROctetString;
import netscape.ldap.ber.stream.BERSequence;
import netscape.ldap.client.JDAPBERTagDecoder;

/**
* Represents an LDAP v3 server control that specifies a simple pagd result
* manipulation, which allows your LDAP client to get entries in multiple chunks
* (The OID for this control is 1.2.840.113556.1.4.319).
* <P>
*
* To use paged search you create a "paged search" control that specifies
* the page size and the cookie from last search. You include the control in a
* search request. When a search is performed only a page is returned to the client
* and a new search has to be performed to access the following elements.
* <P>
*
*
* When constructing an <CODE>LDAPPagedResultsControl</CODE> object,
* you can specify the following information:
* <P>
*
* <UL>
* <LI>the size of the page to be returned
* <LI>the cookie to keep track of the previous page. This is <CODE>null</CODE>
* for the first page and the server returned value for the following.
* </UL>
* <P>
*
* For example:
* <PRE>
* ...
* LDAPConnection ld = new LDAPConnection();
*
* try {
* // Connect to server.
* ld.connect(3,"ds.example.com", 3389, "cn=Directory Manager", "Secret.123");
*
* LDAPPagedResultsControl pagecon = new LDAPPagedResultsControl(false, 3);
* // Set the search constraints to use that control.
* LDAPSearchConstraints cons = new LDAPSearchConstraints();
* cons.setBatchSize(1);
* cons.setServerControls(pagecon);
*
* // Start the paged search.
* String cookie = null;
* int pag = 1;
* do{
* LDAPSearchResults res = ld.search("ou=certificateRepository,ou=ca,dc=ca,dc=pki,dc=example,dc=com",
* LDAPv3.SCOPE_SUB, "(objectClass=certificateRecord)", null, false, cons);
*
* // Loop through the incoming results.
* while (res.hasMoreElements()) {
* LDAPEntry entry = res.next();
* System.out.println("DN: " + entry.getDN());
* }
* for (LDAPControl c: res.getResponseControls()){
* if(c.getID().equals(LDAPPagedResultsControl.PAGEDSEARCH)){
* LDAPPagedResultsControl resC = (LDAPPagedResultsControl) c;
* cookie = resC.getCookie();
* System.out.println("The control for pag " + pag + " return a total or " + resC.getPageSize() + " and cookie " + resC.getCookie());
* if(cookie!=null){
* pagecon = new LDAPPagedResultsControl(false, 3, cookie);
* cons.setServerControls(pagecon);
* }
* }
* }
* pag++;
* } while (cookie != null && !cookie.isEmpty());
* } catch (Exception e) {
* e.printStackTrace();
* }
* </PRE>
*
*
*
* @see netscape.ldap.LDAPControl
*
* @author Marco Fargetta <[email protected]>
*/
public class LDAPPagedResultsControl extends LDAPControl {
public final static String PAGEDSEARCH = "1.2.840.113556.1.4.319";
private int m_pageSize;
private String m_cookie;

/**
* Constructs an <CODE>LDAPPagedResultsControl</CODE> object
* that specifies a paged search.
*
* @param oid the oid of this control
* @param critical <code>true</code> if this control is critical to the search
* @param value the value associated with this control
*
* @see netscape.ldap.LDAPcontrol
*
* @throws LDAPException
* @throws IOException If value contains an invalid BER sequence.
*
* @see netscape.ldap.LDAPControl#register
*/
public LDAPPagedResultsControl(String oid, boolean critical, byte[] vals)
throws LDAPException, IOException {
super(oid, critical, vals);
if(!oid.equals(PAGEDSEARCH)) {
throw new LDAPException("oid must be LDAPPagedResultsControl.PAGEDSEARCH",
LDAPException.PARAM_ERROR);
}

ByteArrayInputStream inStream = new ByteArrayInputStream( vals );
JDAPBERTagDecoder decoder = new JDAPBERTagDecoder();
int[] nRead = { 0 };

/* A sequence */
BERSequence seq = (BERSequence) BERElement.getElement(decoder, inStream, nRead);
m_pageSize = ((BERInteger)seq.elementAt(0)).getValue();
if(seq.size() == 1) {
return;
}
BEROctetString t = (BEROctetString)seq.elementAt(1);
byte[] cookie = t.getValue();
m_cookie = cookie == null ? null : new String(t.getValue(), StandardCharsets.UTF_8);
}

/**
* Constructs an <CODE>LDAPPagedResultsControl</CODE> object without a cookie.
*
* This is equivalent to
* <CODE>LDAPPagedResultsControl(critical, pageSize, null)</CODE>
*
* @param critical <code>true</code> if this control is critical to the search
* @param pageSize the number of entries to be returned with the following
* search request
*
* @see netscape.ldap.LDAPControl
*/
public LDAPPagedResultsControl(boolean critical, int pageSize) {
this(critical, pageSize, null);
}

/**
* Constructs an <CODE>LDAPPagedResultsControl</CODE>.
*
* @param critical <code>true</code> if this control is critical to the search
* @param pageSize the number of entries to be returned with the following
* search request
* @param cookie The cookie to access the next entries. This is an opaque value
* returned by the server or <CODE>null</CODE> for the initial search
*
* @see netscape.ldap.LDAPControl
*/
public LDAPPagedResultsControl(boolean critical, int pageSize, String cookie) {
super(PAGEDSEARCH, critical, null);
this.m_pageSize = pageSize;
this.m_cookie = cookie;
m_value = generateNextPageValues(pageSize, cookie);
}

/**
* Encode the parameters as requested for the control
*
* @param pageSize the number of entries to be returned with the following
* search request
* @param cookie The cookie to access the next entries (it is an opaque value
* returned by the server)
* @return
*/
private byte[] generateNextPageValues(int pageSize, String cookie) {

BERSequence seq = new BERSequence();
seq.addElement(new BERInteger(pageSize));
if(cookie != null) {
seq.addElement(new BEROctetString(cookie));
} else {
seq.addElement(new BEROctetString(""));
}
BEROctetString controlValue = new BEROctetString(flattenBER(seq));
return controlValue.getValue();
}

/**
* Gets the page size for the search request.
*
* @return the number of entries to be returned when a search is requested to the server and
* the number of entries available when returned from the server.
*/
public int getPageSize() {
return m_pageSize;
}

/**
* Gets the cookie for the following search request.
*
* @return the cookie to use for the following request or null if all entries
* have been returned.
*/
public String getCookie() {
return m_cookie;
}
}

0 comments on commit c4f26fc

Please sign in to comment.