From fa51c577650be4708f5ab73f8fd48e7a64ffd8a9 Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 04 May 2012 14:14:59 +0000
Subject: [PATCH] Adding content to the chapter on LDAP controls; not quite done yet

---
 opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java |  493 +++++++++++++++++++++++++-
 opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml                                        |  568 ++++++++++++++++++++++++++++---
 opendj-sdk/opendj3/src/site/resources/Example.ldif                                                    |   18 +
 3 files changed, 982 insertions(+), 97 deletions(-)

diff --git a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
index 5a84fc8..cf464fe 100644
--- a/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
+++ b/opendj-sdk/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -29,17 +29,22 @@
 import java.io.IOException;
 import java.util.Collection;
 
+import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
+import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.ErrorResultException;
 import org.forgerock.opendj.ldap.ErrorResultIOException;
 import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
 import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.RootDSE;
+import org.forgerock.opendj.ldap.SearchResultHandler;
 import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
 import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.opendj.ldap.SortKey;
 import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
 import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl;
 import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl;
@@ -47,13 +52,27 @@
 import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl;
 import org.forgerock.opendj.ldap.controls.ManageDsaITRequestControl;
 import org.forgerock.opendj.ldap.controls.MatchedValuesRequestControl;
+import org.forgerock.opendj.ldap.controls.PasswordExpiredResponseControl;
+import org.forgerock.opendj.ldap.controls.PasswordExpiringResponseControl;
+import org.forgerock.opendj.ldap.controls.PasswordPolicyRequestControl;
+import org.forgerock.opendj.ldap.controls.PasswordPolicyResponseControl;
+import org.forgerock.opendj.ldap.controls.PermissiveModifyRequestControl;
 import org.forgerock.opendj.ldap.controls.PersistentSearchChangeType;
 import org.forgerock.opendj.ldap.controls.PersistentSearchRequestControl;
+import org.forgerock.opendj.ldap.controls.PostReadRequestControl;
+import org.forgerock.opendj.ldap.controls.PostReadResponseControl;
+import org.forgerock.opendj.ldap.controls.PreReadRequestControl;
+import org.forgerock.opendj.ldap.controls.PreReadResponseControl;
+import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
+import org.forgerock.opendj.ldap.controls.ServerSideSortRequestControl;
+import org.forgerock.opendj.ldap.controls.ServerSideSortResponseControl;
+import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl;
 import org.forgerock.opendj.ldap.requests.BindRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldif.ConnectionEntryReader;
@@ -104,9 +123,18 @@
             // For the EntryChangeNotificationResponseControl see
             // usePersistentSearchRequestControl()
             //useGetEffectiveRightsRequestControl(connection);
-            //usePersistentSearchRequestControl(connection);
             //useManageDsaITRequestControl(connection);
-            useMatchedValuesRequestControl(connection);
+            //useMatchedValuesRequestControl(connection);
+            //usePasswordExpiredResponseControl(connection);
+            //usePasswordExpiringResponseControl(connection);
+            //usePasswordPolicyRequestControl(connection);
+            //usePermissiveModifyRequestControl(connection);
+            //usePersistentSearchRequestControl(connection);
+            //usePostReadRequestControl(connection);
+            //usePreReadRequestControl(connection);
+            //useProxiedAuthV2RequestControl(connection);
+            //useServerSideSortRequestControl(connection);
+            useSimplePagedResultsControl(connection);
             // TODO: The rest of the supported controls
 
         } catch (final ErrorResultException e) {
@@ -121,7 +149,8 @@
     }
 
     /**
-     * Use the LDAP assertion control to perform a trivial modification.
+     * Use the LDAP assertion control to modify Babs Jensen's description if
+     * her entry does not have a description, yet.
      *
      * @param connection
      *            Active connection to LDAP server containing <a
@@ -132,25 +161,26 @@
      */
     static void useAssertionControl(Connection connection) throws ErrorResultException {
         if (isSupported(AssertionRequestControl.OID)) {
-            // Modify Babs Jensen's description if her entry does not have
-            // a description, yet.
             final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
 
-            ModifyRequest request = Requests.newModifyRequest(dn);
-            request.addControl(AssertionRequestControl.newControl(true, Filter
-                    .valueOf("!(description=*)")));
-            request.addModification(ModificationType.ADD, "description",
-                    "Created with the help of the LDAP assertion control");
+            final ModifyRequest request =
+                    Requests.newModifyRequest(dn)
+                        .addControl(AssertionRequestControl.newControl(
+                                true, Filter.valueOf("!(description=*)")))
+                        .addModification(ModificationType.ADD, "description",
+                                "Created using LDAP assertion control");
 
             connection.modify(request);
 
-            LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
             try {
                 writer.writeEntry(connection.readEntry(dn, "description"));
                 writer.close();
             } catch (final IOException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("AssertionRequestControl not supported.");
         }
     }
 
@@ -166,12 +196,13 @@
      */
     static void useAuthorizationIdentityRequestControl(Connection connection) throws ErrorResultException {
         if (isSupported(AuthorizationIdentityRequestControl.OID)) {
-            final String name = "uid=bjensen,ou=People,dc=example,dc=com";
-            final char[] password = "hifalutin".toCharArray();
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+            final char[] pwd = "hifalutin".toCharArray();
 
-            System.out.println("Binding as " + name);
-            BindRequest request = Requests.newSimpleBindRequest(name, password);
-            request.addControl(AuthorizationIdentityRequestControl.newControl(true));
+            System.out.println("Binding as " + dn);
+            final BindRequest request =
+                    Requests.newSimpleBindRequest(dn, pwd)
+                        .addControl(AuthorizationIdentityRequestControl.newControl(true));
 
             final BindResult result = connection.bind(request);
             try {
@@ -183,6 +214,8 @@
             } catch (final DecodeException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("AuthorizationIdentityRequestControl not supported.");
         }
     }
 
@@ -202,12 +235,12 @@
         if (isSupported(GetEffectiveRightsRequestControl.OID)) {
             final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com";
 
-            SearchRequest request =
+            final SearchRequest request =
                     Requests.newSearchRequest(
                             "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
-                            "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo");
-            request.addControl(
-                    GetEffectiveRightsRequestControl.newControl(true, authDN, "cn"));
+                            "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo")
+                            .addControl(GetEffectiveRightsRequestControl.newControl(
+                                    true, authDN, "cn"));
 
             final ConnectionEntryReader reader = connection.search(request);
             final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
@@ -226,6 +259,8 @@
             } catch (final IOException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("GetEffectiveRightsRequestControl not supported.");
         }
     }
 
@@ -270,6 +305,8 @@
             } catch (final IOException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("ManageDsaITRequestControl not supported.");
         }
     }
 
@@ -287,11 +324,11 @@
     static void useMatchedValuesRequestControl(Connection connection) throws ErrorResultException {
         if (isSupported(MatchedValuesRequestControl.OID)) {
             final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
-            SearchRequest request =
+            final SearchRequest request =
                     Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
-                            "(objectclass=*)", "cn");
-            final String filter = "cn=Babs Jensen";
-            request.addControl(MatchedValuesRequestControl.newControl(true, filter));
+                            "(objectclass=*)", "cn")
+                            .addControl(MatchedValuesRequestControl.newControl(
+                                    true, "(cn=Babs Jensen)"));
 
             final SearchResultEntry entry = connection.searchSingleEntry(request);
             System.out.println("Reading entry with matched values request.");
@@ -302,6 +339,159 @@
             } catch (final IOException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("MatchedValuesRequestControl not supported.");
+        }
+    }
+
+    /**
+     * Check the Password Expired Response Control. To get this code to output
+     * something, you must first set up an appropriate password policy and wait
+     * for Barbara Jensen's password to expire.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     */
+    static void usePasswordExpiredResponseControl(Connection connection) {
+        if (isSupported(PasswordExpiredResponseControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+            final char[] pwd = "hifalutin".toCharArray();
+
+            try {
+                connection.bind(dn, pwd);
+            } catch (ErrorResultException e) {
+                final Result result = e.getResult();
+                try {
+                    final PasswordExpiredResponseControl control =
+                            result.getControl(PasswordExpiredResponseControl.DECODER,
+                                    new DecodeOptions());
+                    if (!(control == null) && control.hasValue()) {
+                        System.out.println("Password expired for " + dn);
+                    }
+                } catch (DecodeException de) {
+                    de.printStackTrace();
+                }
+            }
+        } else {
+            System.out.println("PasswordExpiredResponseControl not supported.");
+        }
+    }
+
+    /**
+     * Check the Password Expiring Response Control. To get this code to output
+     * something, you must first set up an appropriate password policy and wait
+     * for Barbara Jensen's password to get old enough that the server starts
+     * warning about expiration.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void usePasswordExpiringResponseControl(Connection connection)
+            throws ErrorResultException {
+        if (isSupported(PasswordExpiringResponseControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+            final char[] pwd = "hifalutin".toCharArray();
+
+            final BindResult result = connection.bind(dn, pwd);
+            try {
+                final PasswordExpiringResponseControl control =
+                        result.getControl(PasswordExpiringResponseControl.DECODER,
+                                new DecodeOptions());
+                if (!(control == null) && control.hasValue()) {
+                    System.out.println("Password for " + dn + " expires in "
+                            + control.getSecondsUntilExpiration() + " seconds.");
+                }
+            } catch (DecodeException de) {
+                de.printStackTrace();
+            }
+        } else {
+            System.out.println("PasswordExpiringResponseControl not supported");
+        }
+    }
+
+    /**
+     * Use the Password Policy Request and Response Controls. To get this code
+     * to output something, you must first set up an appropriate password policy
+     * and wait for Barbara Jensen's password to get old enough that the server
+     * starts warning about expiration, or for the password to expire.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     */
+    static void usePasswordPolicyRequestControl(Connection connection) {
+        if (isSupported(PasswordPolicyRequestControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+            final char[] pwd = "hifalutin".toCharArray();
+
+            try {
+                final BindRequest request = Requests.newSimpleBindRequest(dn, pwd)
+                        .addControl(PasswordPolicyRequestControl.newControl(true));
+
+                final BindResult result = connection.bind(request);
+
+                final PasswordPolicyResponseControl control =
+                        result.getControl(PasswordPolicyResponseControl.DECODER,
+                                new DecodeOptions());
+                if (!(control == null) && !(control.getWarningType() == null)) {
+                    System.out.println("Password policy warning "
+                            + control.getWarningType().toString() + ", value "
+                            + control.getWarningValue() + " for " + dn);
+                }
+            } catch (ErrorResultException e) {
+                final Result result = e.getResult();
+                try {
+                    final PasswordPolicyResponseControl control =
+                            result.getControl(PasswordPolicyResponseControl.DECODER,
+                                    new DecodeOptions());
+                    if (!(control == null)) {
+                        System.out.println("Password policy error "
+                                + control.getErrorType().toString() + " for " + dn);
+                    }
+                } catch (DecodeException de) {
+                    de.printStackTrace();
+                }
+            } catch (DecodeException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("PasswordPolicyRequestControl not supported");
+        }
+    }
+
+    /**
+     * Use Permissive Modify Request Control to try to add an attribute that
+     * already exists.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void usePermissiveModifyRequestControl(Connection connection)
+            throws ErrorResultException {
+        if (isSupported(PermissiveModifyRequestControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+            final ModifyRequest request =
+                    Requests.newModifyRequest(dn)
+                        .addControl(PermissiveModifyRequestControl.newControl(true))
+                        .addModification(ModificationType.ADD, "uid", "bjensen");
+
+            connection.modify(request);
+            System.out.println("Permissive modify did not complain about "
+                    + "attempt to add uid: bjensen to " + dn + ".");
+        } else {
+            System.out.println("PermissiveModifyRequestControl not supported");
         }
     }
 
@@ -322,18 +512,16 @@
      */
     static void usePersistentSearchRequestControl(Connection connection) throws ErrorResultException {
         if (isSupported(PersistentSearchRequestControl.OID)) {
-            SearchRequest request =
+            final SearchRequest request =
                     Requests.newSearchRequest(
-                            "dc=example,dc=com",
-                            SearchScope.WHOLE_SUBTREE,
-                            "(objectclass=inetOrgPerson)",
-                            "cn");
-            request.addControl(PersistentSearchRequestControl.newControl(
-                    true, true, true, // isCritical, changesOnly, returnECs
-                    PersistentSearchChangeType.ADD,
-                    PersistentSearchChangeType.DELETE,
-                    PersistentSearchChangeType.MODIFY,
-                    PersistentSearchChangeType.MODIFY_DN));
+                            "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
+                            "(objectclass=inetOrgPerson)", "cn")
+                            .addControl(PersistentSearchRequestControl.newControl(
+                                    true, true, true, // isCritical, changesOnly, returnECs
+                                    PersistentSearchChangeType.ADD,
+                                    PersistentSearchChangeType.DELETE,
+                                    PersistentSearchChangeType.MODIFY,
+                                    PersistentSearchChangeType.MODIFY_DN));
 
             final ConnectionEntryReader reader = connection.search(request);
 
@@ -365,6 +553,241 @@
             } catch (final SearchResultReferenceIOException e) {
                 e.printStackTrace();
             }
+        } else {
+            System.out.println("PersistentSearchRequestControl not supported.");
+        }
+    }
+
+
+    /**
+     * Use Post Read Controls to get entry content after a modification.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void usePostReadRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(PostReadRequestControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+            final ModifyRequest request =
+                    Requests.newModifyRequest(dn)
+                    .addControl(PostReadRequestControl.newControl(true, "description"))
+                    .addModification(ModificationType.REPLACE,
+                            "description", "Using the PostReadRequestControl");
+
+            final Result result = connection.modify(request);
+            try {
+                final PostReadResponseControl control =
+                        result.getControl(PostReadResponseControl.DECODER,
+                                new DecodeOptions());
+                final Entry entry = control.getEntry();
+
+                final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+                writer.writeEntry(entry);
+                writer.close();
+            } catch (DecodeException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("PostReadRequestControl not supported");
+        }
+    }
+
+    /**
+     * Use Pre Read Controls to get entry content before a modification.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void usePreReadRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(PreReadRequestControl.OID)) {
+            final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+            final ModifyRequest request =
+                    Requests.newModifyRequest(dn)
+                    .addControl(PreReadRequestControl.newControl(true, "mail"))
+                    .addModification(
+                            ModificationType.REPLACE, "mail", "modified@example.com");
+
+            final Result result = connection.modify(request);
+            try {
+                final PreReadResponseControl control =
+                        result.getControl(PreReadResponseControl.DECODER,
+                                new DecodeOptions());
+                final Entry entry = control.getEntry();
+
+                final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+                writer.writeEntry(entry);
+                writer.close();
+            } catch (DecodeException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("PreReadRequestControl not supported");
+        }
+    }
+
+    /**
+     * Use proxied authorization to modify an identity as another user.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void useProxiedAuthV2RequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(ProxiedAuthV2RequestControl.OID)) {
+            final String bindDN = "cn=My App,ou=Apps,dc=example,dc=com";
+            final String targetDn = "uid=bjensen,ou=People,dc=example,dc=com";
+            final String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com";
+
+            final ModifyRequest request =
+                    Requests.newModifyRequest(targetDn)
+                    .addControl(ProxiedAuthV2RequestControl.newControl(authzId))
+                    .addModification(ModificationType.REPLACE, "description",
+                            "Done with proxied authz");
+
+            connection.bind(bindDN, "password".toCharArray());
+            connection.modify(request);
+            final Entry entry = connection.readEntry(targetDn, "description");
+
+            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+            try {
+                writer.writeEntry(entry);
+                writer.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("ProxiedAuthV2RequestControl not supported");
+        }
+    }
+
+    /**
+     * Use the server-side sort controls.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void useServerSideSortRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(ServerSideSortRequestControl.OID)) {
+            final SearchRequest request =
+                    Requests.newSearchRequest("dc=example,dc=com",
+                            SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
+                            .addControl(ServerSideSortRequestControl.newControl(
+                                            true, new SortKey("cn")));
+
+            final SearchResultHandler resultHandler = new MySearchResultHandler();
+            final Result result = connection.search(request, resultHandler);
+
+            try {
+                final ServerSideSortResponseControl control =
+                        result.getControl(ServerSideSortResponseControl.DECODER,
+                                new DecodeOptions());
+                if (control != null && control.getResult() == ResultCode.SUCCESS) {
+                    System.out.println("# Entries are sorted.");
+                    // FIXME: But the order is backwards!
+                } else {
+                    System.out.println("# Entries not necessarily sorted");
+                }
+            } catch (DecodeException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("ServerSideSortRequestControl not supported");
+        }
+    }
+
+    private static class MySearchResultHandler implements SearchResultHandler {
+
+        @Override
+        public void handleErrorResult(ErrorResultException error) {
+            // Ignore.
+        }
+
+        @Override
+        public void handleResult(Result result) {
+            // Ignore.
+        }
+
+        @Override
+        public boolean handleEntry(SearchResultEntry entry) {
+            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+            try {
+                writer.writeEntry(entry);
+                writer.flush();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+            return true;
+        }
+
+        @Override
+        public boolean handleReference(SearchResultReference reference) {
+            System.out.println("Got a reference: " + reference.toString());
+            return false;
+        }
+    }
+
+    /**
+     * Use the simple paged results mechanism.
+     *
+     * @param connection
+     *            Active connection to LDAP server containing <a
+     *            href="http://opendj.forgerock.org/Example.ldif"
+     *            >Example.ldif</a> content.
+     * @throws ErrorResultException
+     *             Operation failed.
+     */
+    static void useSimplePagedResultsControl(Connection connection) throws ErrorResultException {
+        if (isSupported(SimplePagedResultsControl.OID)) {
+            ByteString cookie = ByteString.empty();
+            SearchRequest request;
+            final SearchResultHandler resultHandler = new MySearchResultHandler();
+            Result result;
+
+            int page = 1;
+            do {
+                System.out.println("# Simple paged results: Page " + page);
+
+                request =
+                        Requests.newSearchRequest("dc=example,dc=com",
+                                SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
+                                .addControl(SimplePagedResultsControl.newControl(
+                                        true, 3, cookie));
+
+                result = connection.search(request, resultHandler);
+                try {
+                    SimplePagedResultsControl control =
+                            result.getControl(SimplePagedResultsControl.DECODER,
+                                    new DecodeOptions());
+                    cookie = control.getCookie();
+                } catch (DecodeException e) {
+                    e.printStackTrace();
+                }
+
+                ++page;
+            } while (cookie.length() != 0);
+        } else {
+            System.out.println("SimplePagedResultsControl not supported");
         }
     }
 
diff --git a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
index 962cb2e..a9a665e 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
@@ -42,7 +42,7 @@
   <emphasis>request controls</emphasis>, and those sent by servers are termed
   <emphasis>response controls</emphasis>.</para>
  </section>
- 
+
  <section xml:id="get-supported-controls">
   <title>Determining Supported Controls</title>
 
@@ -124,7 +124,7 @@
 }
 </programlisting>
  </section>
- 
+
  <section xml:id="use-assertion-request-control">
   <title>Assertion Request Control</title>
 
@@ -136,31 +136,31 @@
 
   <programlisting language="java">
 if (isSupported(AssertionRequestControl.OID)) {
-    // Modify Babs Jensen's description if her entry does not have
-    // a description, yet.
     final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
 
-    ModifyRequest request = Requests.newModifyRequest(dn);
-    request.addControl(AssertionRequestControl.newControl(true, Filter
-            .valueOf("!(description=*)")));
-    request.addModification(ModificationType.ADD, "description",
-            "Created with the help of the LDAP assertion control");
+    final ModifyRequest request =
+            Requests.newModifyRequest(dn)
+                .addControl(AssertionRequestControl.newControl(
+                        true, Filter.valueOf("!(description=*)")))
+                .addModification(ModificationType.ADD, "description",
+                        "Created using LDAP assertion control");
 
     connection.modify(request);
 
-    LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
     try {
         writer.writeEntry(connection.readEntry(dn, "description"));
         writer.close();
     } catch (final IOException e) {
         e.printStackTrace();
     }
-}</programlisting>
+}
+</programlisting>
 
   <para>OpenDJ directory server supports the LDAP assertion control:</para>
 
   <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com
-description: Created with the help of the LDAP assertion control</programlisting>
+description: Created using LDAP assertion control</programlisting>
  </section>
 
  <section xml:id="use-authorization-identity-control">
@@ -173,12 +173,13 @@
 
   <programlisting language="java">
 if (isSupported(AuthorizationIdentityRequestControl.OID)) {
-    final String name = "uid=bjensen,ou=People,dc=example,dc=com";
-    final char[] password = "hifalutin".toCharArray();
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+    final char[] pwd = "hifalutin".toCharArray();
 
-    System.out.println("Binding as " + name);
-    BindRequest request = Requests.newSimpleBindRequest(name, password);
-    request.addControl(AuthorizationIdentityRequestControl.newControl(true));
+    System.out.println("Binding as " + dn);
+    final BindRequest request =
+            Requests.newSimpleBindRequest(dn, pwd)
+                .addControl(AuthorizationIdentityRequestControl.newControl(true));
 
     final BindResult result = connection.bind(request);
     try {
@@ -190,7 +191,8 @@
     } catch (final DecodeException e) {
         e.printStackTrace();
     }
-}</programlisting>
+}
+</programlisting>
 
   <para>OpenDJ directory server supports the LDAP Authorization Identity
   Controls:</para>
@@ -210,18 +212,16 @@
 
   <programlisting language="java">
 if (isSupported(PersistentSearchRequestControl.OID)) {
-    SearchRequest request =
+    final SearchRequest request =
             Requests.newSearchRequest(
-                    "dc=example,dc=com",
-                    SearchScope.WHOLE_SUBTREE,
-                    "(objectclass=inetOrgPerson)",
-                    "cn");
-    request.addControl(PersistentSearchRequestControl.newControl(
-            true, true, true, // isCritical, changesOnly, returnECs
-            PersistentSearchChangeType.ADD,
-            PersistentSearchChangeType.DELETE,
-            PersistentSearchChangeType.MODIFY,
-            PersistentSearchChangeType.MODIFY_DN));
+                    "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
+                    "(objectclass=inetOrgPerson)", "cn")
+                    .addControl(PersistentSearchRequestControl.newControl(
+                            true, true, true, // critical,changesOnly,returnECs
+                            PersistentSearchChangeType.ADD,
+                            PersistentSearchChangeType.DELETE,
+                            PersistentSearchChangeType.MODIFY,
+                            PersistentSearchChangeType.MODIFY_DN));
 
     final ConnectionEntryReader reader = connection.search(request);
 
@@ -267,8 +267,14 @@
 Change type: modifyDN
 Previous DN: uid=abarnes,ou=People,dc=example,dc=com
 Change number: -1</programlisting>
+
+  <para>In this case, <literal>Change number: -1</literal> because the server
+  did not set a change number value. OpenDJ directory server does not set the
+  change number value in the response control. If you need to track the order
+  of changes with OpenDJ directory server, read the external change log instead
+  of using the entry change notification response control.</para>
  </section>
- 
+
  <section xml:id="use-get-effective-rights-control">
   <title>GetEffectiveRights Request Control</title>
 
@@ -282,12 +288,12 @@
 if (isSupported(GetEffectiveRightsRequestControl.OID)) {
     final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com";
 
-    SearchRequest request =
+    final SearchRequest request =
             Requests.newSearchRequest(
                     "dc=example,dc=com", SearchScope.WHOLE_SUBTREE,
-                    "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo");
-    request.addControl(
-            GetEffectiveRightsRequestControl.newControl(true, authDN, "cn"));
+                    "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo")
+                    .addControl(GetEffectiveRightsRequestControl.newControl(
+                            true, authDN, "cn"));
 
     final ConnectionEntryReader reader = connection.search(request);
     final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
@@ -371,7 +377,7 @@
  ) ( reason: evaluated allow , deciding_aci: allow all Admin group)
 </programlisting>
  </section>
- 
+
  <section xml:id="use-managedsait-control">
   <title>ManageDsaIT Request Control</title>
 
@@ -417,7 +423,7 @@
 
   <para>OpenDJ directory server supports the ManageDsaIT Request Control.</para>
  </section>
- 
+
  <section xml:id="use-matched-values-request-control">
   <title>Matched Values Request Control</title>
 
@@ -435,11 +441,11 @@
   <programlisting language="java">
 if (isSupported(MatchedValuesRequestControl.OID)) {
     final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
-    SearchRequest request =
+    final SearchRequest request =
             Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
-                    "(objectclass=*)", "cn");
-    final String filter = "cn=Babs Jensen";
-    request.addControl(MatchedValuesRequestControl.newControl(true, filter));
+                    "(objectclass=*)", "cn")
+                    .addControl(MatchedValuesRequestControl.newControl(
+                            true, "(cn=Babs Jensen)"));
 
     final SearchResultEntry entry = connection.searchSingleEntry(request);
     System.out.println("Reading entry with matched values request.");
@@ -461,73 +467,511 @@
 cn: Babs Jensen
 </programlisting>
  </section>
- 
+
  <section xml:id="use-password-expired-control">
   <title>Password Expired Response Control</title>
-  <para>TODO</para>
+
+  <para>A directory server can return the Password Expired Response Control,
+  described in the Internet-Draft <link xlink:show="new"
+  xlink:href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy"><citetitle
+  >Password Policy for LDAP Directories</citetitle></link>, when a bind fails
+  because the password has expired. In order to see this, you must configure
+  the directory to expire Barbara Jensen's password.</para>
+
+  <programlisting language="java">
+if (isSupported(PasswordExpiredResponseControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+    final char[] pwd = "hifalutin".toCharArray();
+
+    try {
+        connection.bind(dn, pwd);
+    } catch (ErrorResultException e) {
+        final Result result = e.getResult();
+        try {
+            final PasswordExpiredResponseControl control =
+                    result.getControl(PasswordExpiredResponseControl.DECODER,
+                            new DecodeOptions());
+            if (!(control == null) &amp;&amp; control.hasValue()) {
+                System.out.println("Password expired for " + dn);
+            }
+        } catch (DecodeException de) {
+            de.printStackTrace();
+        }
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the Password Expired Response Control.
+  To obtain the following output from the excerpt, you can change the default
+  password policy configuration to set a short maximum password age, change
+  Barbara Jensen's password, and wait for it to expire.</para>
+
+  <programlisting
+  >Password expired for uid=bjensen,ou=People,dc=example,dc=com</programlisting>
  </section>
- 
+
  <section xml:id="use-password-expiring-control">
   <title>Password Expiring Response Control</title>
-  <para>TODO</para>
+
+  <para>The Password Expiring Response Control, described in the Internet-Draft
+  <link xlink:href="http://tools.ietf.org/html/draft-vchu-ldap-pwd-policy"
+  xlink:show="new" ><citetitle>Password Policy for LDAP
+  Directories</citetitle></link>, warns your application during a bind
+  that the password used will soon expire.</para>
+
+  <programlisting language="java">
+if (isSupported(PasswordExpiringResponseControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+    final char[] pwd = "hifalutin".toCharArray();
+
+    final BindResult result = connection.bind(dn, pwd);
+    try {
+        final PasswordExpiringResponseControl control =
+                result.getControl(PasswordExpiringResponseControl.DECODER,
+                        new DecodeOptions());
+        if (!(control == null) &amp;&amp; control.hasValue()) {
+            System.out.println("Password for " + dn + " expires in "
+                    + control.getSecondsUntilExpiration() + " seconds.");
+        }
+    } catch (DecodeException de) {
+        de.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the Password Expiring Response Control.
+  To obtain the following output from the excerpt, you can change the default
+  password policy configuration to set a maximum password age and a warning
+  interval, change Barbara Jensen's password, and wait until you enter the
+  warning interval before password expiration.</para>
+
+  <programlisting>Password for uid=bjensen,ou=People,dc=example,dc=com
+ expires in 237 seconds.</programlisting>
  </section>
  
  <section xml:id="use-password-policy-controls">
   <title>Password Policy Controls</title>
-  <para>TODO</para>
+
+  <para>The Behera Internet-Draft, <link xlink:show="new"
+  xlink:href="http://tools.ietf.org/html/draft-behera-ldap-password-policy"
+  ><citetitle>Password Policy for LDAP Directories</citetitle></link>, describes
+  Password Policy Request and Response Controls. You send the request control
+  with a request to let the directory server know that your application can
+  handle the response control. The directory server sends the response control
+  on applicable operations to communicate warnings and errors.</para>
+
+  <programlisting language="java">
+if (isSupported(PasswordPolicyRequestControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+    final char[] pwd = "hifalutin".toCharArray();
+
+    try {
+        final BindRequest request = Requests.newSimpleBindRequest(dn, pwd)
+                .addControl(PasswordPolicyRequestControl.newControl(true));
+
+        final BindResult result = connection.bind(request);
+
+        final PasswordPolicyResponseControl control =
+                result.getControl(PasswordPolicyResponseControl.DECODER,
+                        new DecodeOptions());
+        if (!(control == null) &amp;&amp; !(control.getWarningType() == null)) {
+            System.out.println("Password policy warning "
+                    + control.getWarningType().toString() + ", value "
+                    + control.getWarningValue() + " for " + dn);
+        }
+    } catch (ErrorResultException e) {
+        final Result result = e.getResult();
+        try {
+            final PasswordPolicyResponseControl control =
+                    result.getControl(PasswordPolicyResponseControl.DECODER,
+                            new DecodeOptions());
+            if (!(control == null)) {
+                System.out.println("Password policy error "
+                        + control.getErrorType().toString() + " for " + dn);
+            }
+        } catch (DecodeException de) {
+            de.printStackTrace();
+        }
+    } catch (DecodeException e) {
+        e.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the Password Policy Controls. To obtain
+  the output from the excerpt, you can change the default password policy
+  configuration to set a maximum password age and a warning interval, change
+  Barbara Jensen's password, and then run the example during the warning
+  interval and after the password has expired.</para>
+
+  <para>For a warning:</para>
+  <programlisting>Password policy warning timeBeforeExpiration, value 237 for
+ uid=bjensen,ou=People,dc=example,dc=com</programlisting>
+
+  <para>For an error:</para>
+  <programlisting>Password policy error passwordExpired for
+ uid=bjensen,ou=People,dc=example,dc=com</programlisting>
  </section>
- 
+
  <section xml:id="use-permissive-modify-request-control">
   <title>Permissive Modify Request Control</title>
-  <para>TODO</para>
+
+  <para>Microsoft defined a Permissive Modify Request Control that relaxes
+  some constraints when your application performs a modify operation and
+  tries to <literal>add</literal> an attribute that already exists, or to
+  <literal>delete</literal> an attribute that does not exist.</para>
+
+  <programlisting language="java">
+if (isSupported(PermissiveModifyRequestControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+    final ModifyRequest request =
+            Requests.newModifyRequest(dn)
+                .addControl(PermissiveModifyRequestControl.newControl(true))
+                .addModification(ModificationType.ADD, "uid", "bjensen");
+
+    connection.modify(request);
+    System.out.println("Permissive modify did not complain about "
+            + "attempt to add uid: bjensen to " + dn + ".");
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the Permissive Modify Request
+  Control:</para>
+
+  <programlisting>Permissive modify did not complain about attempt to add
+ uid: bjensen to uid=bjensen,ou=People,dc=example,dc=com.</programlisting>
  </section>
- 
+
  <section xml:id="use-persistent-search-request-control">
   <title>Persistent Search Request Control</title>
 
   <para>See <xref linkend="use-entry-change-notification-control" />.</para>
  </section>
- 
+
  <section xml:id="use-post-read-control">
   <title>Post-Read Controls</title>
-  <para>TODO</para>
+
+  <para>RFC 4527, <link xlink:href="http://tools.ietf.org/html/rfc4527"
+  xlink:show="new"><citetitle>LDAP Read Entry Controls</citetitle></link>,
+  describes the post-read controls that let your application get the content
+  of an entry immediately after modifications are applied.</para>
+
+  <programlisting language="java">
+if (isSupported(PostReadRequestControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+    final ModifyRequest request =
+            Requests.newModifyRequest(dn)
+            .addControl(PostReadRequestControl.newControl(true, "description"))
+            .addModification(ModificationType.REPLACE,
+                    "description", "Using the PostReadRequestControl");
+
+    final Result result = connection.modify(request);
+    try {
+        final PostReadResponseControl control =
+                result.getControl(PostReadResponseControl.DECODER,
+                        new DecodeOptions());
+        final Entry entry = control.getEntry();
+
+        final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+        writer.writeEntry(entry);
+        writer.close();
+    } catch (DecodeException e) {
+        e.printStackTrace();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports these controls:</para>
+
+  <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com
+description: Using the PostReadRequestControl</programlisting>
  </section>
- 
+
  <section xml:id="use-pre-read-control">
   <title>Pre-Read Controls</title>
-  <para>TODO</para>
+
+  <para>RFC 4527, <link xlink:href="http://tools.ietf.org/html/rfc4527"
+  xlink:show="new"><citetitle>LDAP Read Entry Controls</citetitle></link>,
+  describes the pre-read controls that let your application get the content
+  of an entry immediately before modifications are applied.</para>
+
+  <programlisting language="java">
+if (isSupported(PreReadRequestControl.OID)) {
+    final String dn = "uid=bjensen,ou=People,dc=example,dc=com";
+
+    final ModifyRequest request =
+            Requests.newModifyRequest(dn)
+            .addControl(PreReadRequestControl.newControl(true, "mail"))
+            .addModification(
+                    ModificationType.REPLACE, "mail", "modified@example.com");
+
+    final Result result = connection.modify(request);
+    try {
+        final PreReadResponseControl control =
+                result.getControl(PreReadResponseControl.DECODER,
+                        new DecodeOptions());
+        final Entry entry = control.getEntry();
+
+        final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+        writer.writeEntry(entry);
+        writer.close();
+    } catch (DecodeException e) {
+        e.printStackTrace();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports these controls:</para>
+
+  <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com
+mail: bjensen@example.com</programlisting>
  </section>
- 
+
  <section xml:id="use-proxy-authz-control">
   <title>Proxied Authorization Request Controls</title>
-  <para>TODO</para>
+
+  <para>Proxied authorization provides a standard control as defined in
+  <link xlink:href="http://tools.ietf.org/html/rfc4370" xlink:show="new">RFC
+  4370</link> (and an earlier Internet-Draft) for binding with the user
+  credentials of a proxy, who carries out LDAP operations on behalf of other
+  users. You might use proxied authorization, for example, to have your
+  application bind with its credentials, and then carry out operations as the
+  users who login to the application.</para>
+
+  <programlisting language="java">
+if (isSupported(ProxiedAuthV2RequestControl.OID)) {
+    final String bindDN = "cn=My App,ou=Apps,dc=example,dc=com";
+    final String targetDn = "uid=bjensen,ou=People,dc=example,dc=com";
+    final String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com";
+
+    final ModifyRequest request =
+            Requests.newModifyRequest(targetDn)
+            .addControl(ProxiedAuthV2RequestControl.newControl(authzId))
+            .addModification(ModificationType.REPLACE, "description",
+                    "Done with proxied authz");
+
+    connection.bind(bindDN, "password".toCharArray());
+    connection.modify(request);
+    final Entry entry = connection.readEntry(targetDn, "description");
+
+    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+    try {
+        writer.writeEntry(entry);
+        writer.close();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}</programlisting>
+
+  <para>OpenDJ supports proxied authorization, and the example works with the
+  sample data:</para>
+
+  <programlisting language="ldif">dn: uid=bjensen,ou=People,dc=example,dc=com
+description: Done with proxied authz</programlisting>
  </section>
- 
+
  <section xml:id="use-server-side-sort-control">
   <title>Server-Side Sort Controls</title>
-  <para>TODO</para>
+
+  <para>The server-side sort controls are described in RFC 2891, <link
+  xlink:show="new" xlink:href="http://tools.ietf.org/html/rfc2891"><citetitle
+  >LDAP Control Extension for Server Side Sorting of Search
+  Results</citetitle></link>. If possible, sort on the client side instead to
+  reduce load on the server. If not, then you can request a server-side
+  sort.</para>
+
+  <programlisting language="java">
+static void useServerSideSortRequestControl(Connection connection)
+        throws ErrorResultException {
+    if (isSupported(ServerSideSortRequestControl.OID)) {
+        final SearchRequest request =
+                Requests.newSearchRequest("dc=example,dc=com",
+                        SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
+                        .addControl(ServerSideSortRequestControl.newControl(
+                                        true, new SortKey("cn")));
+
+        final SearchResultHandler resultHandler = new MySearchResultHandler();
+        final Result result = connection.search(request, resultHandler);
+
+        try {
+            final ServerSideSortResponseControl control =
+                    result.getControl(ServerSideSortResponseControl.DECODER,
+                            new DecodeOptions());
+            if (control != null &amp;&amp; control.getResult() == ResultCode.SUCCESS) {
+                System.out.println("# Entries are sorted.");
+                // FIXME: But the order is backwards!
+            } else {
+                System.out.println("# Entries not necessarily sorted");
+            }
+        } catch (DecodeException e) {
+            e.printStackTrace();
+        }
+    } else {
+        System.out.println("ServerSideSortRequestControl not supported");
+    }
+}
+
+private static class MySearchResultHandler implements SearchResultHandler {
+
+    @Override
+    public void handleErrorResult(ErrorResultException error) {
+        // Ignore.
+    }
+
+    @Override
+    public void handleResult(Result result) {
+        // Ignore.
+    }
+
+    @Override
+    public boolean handleEntry(SearchResultEntry entry) {
+        final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+        try {
+            writer.writeEntry(entry);
+            writer.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean handleReference(SearchResultReference reference) {
+        System.out.println("Got a reference: " + reference.toString());
+        return false;
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports server-side sorting:</para>
+
+  <programlisting language="ldif">dn: uid=ajensen,ou=People,dc=example,dc=com
+cn: Allison Jensen
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+
+dn: uid=bjense2,ou=People,dc=example,dc=com
+cn: Bjorn Jensen
+
+dn: uid=gjensen,ou=People,dc=example,dc=com
+cn: Gern Jensen
+
+dn: uid=jjensen,ou=People,dc=example,dc=com
+cn: Jody Jensen
+
+dn: uid=kjensen,ou=People,dc=example,dc=com
+cn: Kurt Jensen
+
+dn: uid=rjense2,ou=People,dc=example,dc=com
+cn: Randy Jensen
+
+dn: uid=rjensen,ou=People,dc=example,dc=com
+cn: Richard Jensen
+
+dn: uid=tjensen,ou=People,dc=example,dc=com
+cn: Ted Jensen
+
+# Entries are sorted.</programlisting>
  </section>
- 
+
  <section xml:id="use-simple-paged-results-control">
   <title>Simple Paged Results Control</title>
-  <para>TODO</para>
+
+  <para>RFC 2696, <link xlink:href="http://tools.ietf.org/html/rfc2696"
+  xlink:show="new"><citetitle>LDAP Control Extension for Simple Paged Results
+  Manipulation</citetitle></link>, defines a control for simple paging of
+  search results that works with a cookie mechanism.</para>
+
+  <programlisting language="java">
+if (isSupported(SimplePagedResultsControl.OID)) {
+    ByteString cookie = ByteString.empty();
+    SearchRequest request;
+    final SearchResultHandler resultHandler = new MySearchResultHandler();
+    Result result;
+
+    int page = 1;
+    do {
+        System.out.println("# Simple paged results: Page " + page);
+
+        request =
+                Requests.newSearchRequest("dc=example,dc=com",
+                        SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
+                        .addControl(SimplePagedResultsControl.newControl(
+                                true, 3, cookie));
+
+        result = connection.search(request, resultHandler);
+        try {
+            SimplePagedResultsControl control =
+                    result.getControl(SimplePagedResultsControl.DECODER,
+                            new DecodeOptions());
+            cookie = control.getCookie();
+        } catch (DecodeException e) {
+            e.printStackTrace();
+        }
+
+        ++page;
+    } while (cookie.length() != 0);
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports getting simple paged results:</para>
+
+  <programlisting language="ldif"># Simple paged results: Page 1
+dn: uid=ajensen,ou=People,dc=example,dc=com
+cn: Allison Jensen
+
+dn: uid=bjense2,ou=People,dc=example,dc=com
+cn: Bjorn Jensen
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+
+# Simple paged results: Page 2
+dn: uid=gjensen,ou=People,dc=example,dc=com
+cn: Gern Jensen
+
+dn: uid=jjensen,ou=People,dc=example,dc=com
+cn: Jody Jensen
+
+dn: uid=kjensen,ou=People,dc=example,dc=com
+cn: Kurt Jensen
+
+# Simple paged results: Page 3
+dn: uid=rjense2,ou=People,dc=example,dc=com
+cn: Randy Jensen
+
+dn: uid=rjensen,ou=People,dc=example,dc=com
+cn: Richard Jensen
+
+dn: uid=tjensen,ou=People,dc=example,dc=com
+cn: Ted Jensen
+</programlisting>
  </section>
- 
+
  <section xml:id="use-subentry-request-control">
   <title>Sub-entries Request Control</title>
   <para>TODO</para>
  </section>
- 
+
  <section xml:id="use-subtree-delete-control">
   <title>Subtree Delete Request Control</title>
   <para>TODO</para>
  </section>
- 
+
  <section xml:id="use-vlv-control">
   <title>Virtual List View Controls</title>
   <para>TODO</para>
  </section>
- 
+
  <section xml:id="custom-control">
   <title>Custom Controls</title>
   <para>TODO</para>
diff --git a/opendj-sdk/opendj3/src/site/resources/Example.ldif b/opendj-sdk/opendj3/src/site/resources/Example.ldif
index 2cb72a1..71bc402 100644
--- a/opendj-sdk/opendj3/src/site/resources/Example.ldif
+++ b/opendj-sdk/opendj3/src/site/resources/Example.ldif
@@ -61,6 +61,9 @@
 aci: (target="ldap:///dc=example,dc=com") (targetattr =
  "*")(version 3.0; acl "allow all Admin group"; allow(all) groupdn =
  "ldap:///cn=Directory Administrators,ou=Groups,dc=example,dc=com";)
+aci: (target="ldap:///dc=example,dc=com") (targetattr ="*
+ ")(version 3.0; acl "Allow apps proxied auth"; allow(all, proxy
+ )(userdn = "ldap:///cn=*,ou=Apps,dc=example,dc=com");)
 
 dn: ou=Company Servers,dc=example,dc=com
 objectClass: organizationalUnit
@@ -3756,3 +3759,18 @@
 objectClass: top
 ref: ldap:///ou=People,dc=example,dc=com
 
+dn: ou=Apps,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: top
+ou: Apps
+
+dn: cn=My App,ou=Apps,dc=example,dc=com
+cn: My App
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: top
+sn: App
+userPassword: password
+ds-privilege-name: proxied-auth
+

--
Gitblit v1.10.0