From 82977c03ecd4a686dc25fd6071348da34b00e224 Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Mon, 07 May 2012 13:27:36 +0000
Subject: [PATCH] Additional content for controls chapter

---
 opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java |  152 ++++++++++++++++++++-
 opendj3/src/site/resources/Example.ldif                                                    |   77 ++++++++--
 opendj3/src/main/docbkx/dev-guide/chap-controls.xml                                        |  174 +++++++++++++++++++++++-
 3 files changed, 368 insertions(+), 35 deletions(-)

diff --git a/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
index cf464fe..ce17ea2 100644
--- a/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
+++ b/opendj3/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -67,7 +67,12 @@
 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.controls.SubentriesRequestControl;
+import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl;
+import org.forgerock.opendj.ldap.controls.VirtualListViewRequestControl;
+import org.forgerock.opendj.ldap.controls.VirtualListViewResponseControl;
 import org.forgerock.opendj.ldap.requests.BindRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
@@ -116,10 +121,10 @@
             final char[] password = "password".toCharArray();
             connection.bind(user, password);
 
-            // Uncomment one of the methods:
+            // Uncomment a method to run one of the examples.
 
             //useAssertionControl(connection);
-            //useAuthorizationIdentityRequestControl(connection);
+            useAuthorizationIdentityRequestControl(connection);
             // For the EntryChangeNotificationResponseControl see
             // usePersistentSearchRequestControl()
             //useGetEffectiveRightsRequestControl(connection);
@@ -134,8 +139,10 @@
             //usePreReadRequestControl(connection);
             //useProxiedAuthV2RequestControl(connection);
             //useServerSideSortRequestControl(connection);
-            useSimplePagedResultsControl(connection);
-            // TODO: The rest of the supported controls
+            //useSimplePagedResultsControl(connection);
+            //useSubentriesRequestControl(connection);
+            //useSubtreeDeleteRequestControl(connection);
+            //useVirtualListViewRequestControl(connection);
 
         } catch (final ErrorResultException e) {
             System.err.println(e.getMessage());
@@ -277,14 +284,13 @@
      */
     static void useManageDsaITRequestControl(Connection connection) throws ErrorResultException {
         if (isSupported(ManageDsaITRequestControl.OID)) {
-            // This entry is a referral object:
-            final String dn = "dc=references,dc=example,dc=com";
+            final String dn = "dc=ref,dc=com";
 
             final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
             try {
                 System.out.println("Referral without the ManageDsaIT control.");
                 SearchRequest request = Requests.newSearchRequest(dn,
-                        SearchScope.BASE_OBJECT, "(objectclass=*)", "");
+                        SearchScope.SUBORDINATES, "(objectclass=*)", "");
                 final ConnectionEntryReader reader = connection.search(request);
                 while (reader.hasNext()) {
                     if (reader.isReference()) {
@@ -690,7 +696,7 @@
     static void useServerSideSortRequestControl(Connection connection) throws ErrorResultException {
         if (isSupported(ServerSideSortRequestControl.OID)) {
             final SearchRequest request =
-                    Requests.newSearchRequest("dc=example,dc=com",
+                    Requests.newSearchRequest("ou=People,dc=example,dc=com",
                             SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
                             .addControl(ServerSideSortRequestControl.newControl(
                                             true, new SortKey("cn")));
@@ -704,7 +710,6 @@
                                 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");
                 }
@@ -792,6 +797,135 @@
     }
 
     /**
+     * Use the subentries request control.
+     *
+     * @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 useSubentriesRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(SubentriesRequestControl.OID)) {
+            final SearchRequest request =
+                    Requests.newSearchRequest("dc=example,dc=com",
+                                SearchScope.WHOLE_SUBTREE,
+                                "cn=*Class of Service", "*", "+")
+                            .addControl(SubentriesRequestControl.newControl(
+                                true, true));
+
+            final ConnectionEntryReader reader = connection.search(request);
+            final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+            try {
+                while (reader.hasNext()) {
+                    if (reader.isEntry()) {
+                        final SearchResultEntry entry = reader.readEntry();
+                        writer.writeEntry(entry);
+                    }
+                }
+                writer.close();
+            } catch (ErrorResultIOException e) {
+                e.printStackTrace();
+            } catch (SearchResultReferenceIOException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("SubentriesRequestControl not supported");
+        }
+    }
+
+    /**
+     * Use the subtree delete control.
+     *
+     * @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 useSubtreeDeleteRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(SubtreeDeleteRequestControl.OID)) {
+
+            final String dn = "ou=Apps,dc=example,dc=com";
+            final DeleteRequest request =
+                    Requests.newDeleteRequest(dn)
+                            .addControl(SubtreeDeleteRequestControl.newControl(true));
+
+            final Result result = connection.delete(request);
+            if (result.isSuccess()) {
+                System.out.println("Successfully deleted " + dn
+                        + " and all entries below.");
+            } else {
+                System.out.println("Result: " + result.getDiagnosticMessage());
+            }
+        } else {
+            System.out.println("SubtreeDeleteRequestControl not supported");
+        }
+    }
+
+    /**
+     * Use the virtual list view controls. In order to set up OpenDJ directory
+     * server to produce the following output with the example code, use OpenDJ
+     * Control Panel &gt; Manage Indexes &gt; New VLV Index... to set up a
+     * virtual list view index for people by last name, using the filter
+     * {@code (|(givenName=*)(sn=*))}, and sorting first by surname, {@code sn},
+     * in ascending order, then by given name also in ascending order
+     *
+     * @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 useVirtualListViewRequestControl(Connection connection) throws ErrorResultException {
+        if (isSupported(VirtualListViewRequestControl.OID)) {
+            ByteString contextID = ByteString.empty();
+
+            // Add a window of 2 entries on either side of the first sn=Jensen entry.
+            final SearchRequest request =
+                    Requests.newSearchRequest("ou=People,dc=example,dc=com",
+                            SearchScope.WHOLE_SUBTREE, "(sn=*)", "sn", "givenName")
+                            .addControl(ServerSideSortRequestControl.newControl(
+                                    true, new SortKey("sn")))
+                            .addControl(
+                                    VirtualListViewRequestControl.newAssertionControl(
+                                            true,
+                                            ByteString.valueOf("Jensen"),
+                                            2, 2, contextID));
+
+            final SearchResultHandler resultHandler = new MySearchResultHandler();
+            final Result result = connection.search(request, resultHandler);
+
+            try {
+                final ServerSideSortResponseControl sssControl =
+                        result.getControl(ServerSideSortResponseControl.DECODER,
+                                new DecodeOptions());
+                if (sssControl != null && sssControl.getResult() == ResultCode.SUCCESS) {
+                    System.out.println("# Entries are sorted.");
+                } else {
+                    System.out.println("# Entries not necessarily sorted");
+                }
+
+                final VirtualListViewResponseControl vlvControl =
+                        result.getControl(VirtualListViewResponseControl.DECODER,
+                                new DecodeOptions());
+                System.out.println("# Position in list: "
+                        + vlvControl.getTargetPosition() + "/"
+                        + vlvControl.getContentCount());
+            } catch (DecodeException e) {
+                e.printStackTrace();
+            }
+        } else {
+            System.out.println("VirtualListViewRequestControl not supported");
+        }
+    }
+
+    /**
      * Controls supported by the LDAP server.
      */
     private static Collection<String> controls;
diff --git a/opendj3/src/main/docbkx/dev-guide/chap-controls.xml b/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
index a9a665e..e43f98a 100644
--- a/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
+++ b/opendj3/src/main/docbkx/dev-guide/chap-controls.xml
@@ -390,14 +390,13 @@
 
   <programlisting language="java">
 if (isSupported(ManageDsaITRequestControl.OID)) {
-    // This entry is a referral object:
-    final String dn = "dc=references,dc=example,dc=com";
+    final String dn = "dc=ref,dc=com";
 
     final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
     try {
         System.out.println("Referral without the ManageDsaIT control.");
         SearchRequest request = Requests.newSearchRequest(dn,
-                SearchScope.BASE_OBJECT, "(objectclass=*)", "");
+                SearchScope.SUBORDINATES, "(objectclass=*)", "");
         final ConnectionEntryReader reader = connection.search(request);
         while (reader.hasNext()) {
             if (reader.isReference()) {
@@ -409,7 +408,7 @@
         System.out.println("Referral with the ManageDsaIT control.");
         request.addControl(ManageDsaITRequestControl.newControl(true));
         final SearchResultEntry entry = connection.searchSingleEntry(request);
-        writer.writeEntry(entry)
+        writer.writeEntry(entry);
         writer.close();
     } catch (final ErrorResultIOException e) {
         e.printStackTrace();
@@ -421,7 +420,14 @@
 }
 </programlisting>
 
-  <para>OpenDJ directory server supports the ManageDsaIT Request Control.</para>
+  <para>OpenDJ directory server supports the ManageDsaIT Request Control. To use
+  the example entry create a new base DN, <literal>dc=ref,dc=com</literal>
+  before you import the data:</para>
+
+  <programlisting>Referral without the ManageDsaIT control.
+Reference: [ldap:///dc=example,dc=com??sub?]
+Referral with the ManageDsaIT control.
+dn: dc=references,dc=ref,dc=com</programlisting>
  </section>
 
  <section xml:id="use-matched-values-request-control">
@@ -791,7 +797,7 @@
         throws ErrorResultException {
     if (isSupported(ServerSideSortRequestControl.OID)) {
         final SearchRequest request =
-                Requests.newSearchRequest("dc=example,dc=com",
+                Requests.newSearchRequest("ou=People,dc=example,dc=com",
                         SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn")
                         .addControl(ServerSideSortRequestControl.newControl(
                                         true, new SortKey("cn")));
@@ -805,7 +811,6 @@
                             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");
             }
@@ -958,18 +963,165 @@
  </section>
 
  <section xml:id="use-subentry-request-control">
-  <title>Sub-entries Request Control</title>
-  <para>TODO</para>
+  <title>Subentries Request Control</title>
+
+  <para>RFC 3672, <link xlink:href="http://tools.ietf.org/html/rfc3672"
+  xlink:show="new"><citetitle>Subentries in LDAP</citetitle></link>, describes
+  subentries and also the subentries request control. When you perform a search
+  without the control and visibility set to <literal>TRUE</literal>, subentries
+  are only visible in searches with
+  <literal>SearchScope.BASE_OBJECT</literal>.</para>
+
+  <programlisting language="java">
+if (isSupported(SubentriesRequestControl.OID)) {
+    final SearchRequest request =
+            Requests.newSearchRequest("dc=example,dc=com",
+                        SearchScope.WHOLE_SUBTREE,
+                        "cn=*Class of Service", "*", "+")
+                    .addControl(SubentriesRequestControl.newControl(
+                        true, true));
+
+    final ConnectionEntryReader reader = connection.search(request);
+    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+    try {
+        while (reader.hasNext()) {
+            if (reader.isEntry()) {
+                final SearchResultEntry entry = reader.readEntry();
+                writer.writeEntry(entry);
+            }
+        }
+        writer.close();
+    } catch (ErrorResultIOException e) {
+        e.printStackTrace();
+    } catch (SearchResultReferenceIOException e) {
+        e.printStackTrace();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the control.</para>
+
+  <programlisting>TODO: pending OPENDJ-486</programlisting>
  </section>
 
  <section xml:id="use-subtree-delete-control">
   <title>Subtree Delete Request Control</title>
-  <para>TODO</para>
+
+  <para>The subtree delete request control, described in the Internet-Draft
+  <link xlink:href="http://tools.ietf.org/html/draft-armijo-ldap-treedelete"
+  xlink:show="new"><citetitle>Tree Delete Control</citetitle></link>, lets
+  your application delete an entire branch of entries starting with the entry
+  you target for deletion.</para>
+
+  <programlisting language="java">
+if (isSupported(SubtreeDeleteRequestControl.OID)) {
+
+    final String dn = "ou=Apps,dc=example,dc=com";
+    final DeleteRequest request =
+            Requests.newDeleteRequest(dn)
+                    .addControl(SubtreeDeleteRequestControl.newControl(true));
+
+    final Result result = connection.delete(request);
+    if (result.isSuccess()) {
+        System.out.println("Successfully deleted " + dn
+                + " and all entries below.");
+    } else {
+        System.out.println("Result: " + result.getDiagnosticMessage());
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the subtree delete control:</para>
+
+  <programlisting
+  >Successfully deleted ou=Apps,dc=example,dc=com and all entries below.</programlisting>
  </section>
 
  <section xml:id="use-vlv-control">
   <title>Virtual List View Controls</title>
-  <para>TODO</para>
+
+  <para>The virtual list view controls are intended to be used by applications
+  that let users browse lists of directory entries. The Internet-Draft <link
+  xlink:href="http://tools.ietf.org/html/draft-ietf-ldapext-ldapv3-vlv"
+  xlink:show="new"><citetitle>LDAP Extensions for Scrolling View Browsing of
+  Search Results</citetitle></link> describes the controls. The virtual list
+  view request control is used in conjunction with the server-side sort
+  control such that the subset of entries the directory server returns from
+  a search are a window into the full sorted list.</para>
+
+  <programlisting language="java">
+if (isSupported(VirtualListViewRequestControl.OID)) {
+    ByteString contextID = ByteString.empty();
+
+    // Add a window of 2 entries on either side of the first sn=Jensen entry.
+    final SearchRequest request =
+            Requests.newSearchRequest("ou=People,dc=example,dc=com",
+                    SearchScope.WHOLE_SUBTREE, "(sn=*)", "sn", "givenName")
+                    .addControl(ServerSideSortRequestControl.newControl(
+                            true, new SortKey("sn")))
+                    .addControl(
+                            VirtualListViewRequestControl.newAssertionControl(
+                                    true,
+                                    ByteString.valueOf("Jensen"),
+                                    2, 2, contextID));
+
+    final SearchResultHandler resultHandler = new MySearchResultHandler();
+    final Result result = connection.search(request, resultHandler);
+
+    try {
+        final ServerSideSortResponseControl sssControl =
+                result.getControl(ServerSideSortResponseControl.DECODER,
+                        new DecodeOptions());
+        if (sssControl != null &amp;&amp; sssControl.getResult() == ResultCode.SUCCESS){
+            System.out.println("# Entries are sorted.");
+        } else {
+            System.out.println("# Entries not necessarily sorted");
+        }
+
+        final VirtualListViewResponseControl vlvControl =
+                result.getControl(VirtualListViewResponseControl.DECODER,
+                        new DecodeOptions());
+        System.out.println("# Position in list: "
+                + vlvControl.getTargetPosition() + "/"
+                + vlvControl.getContentCount());
+    } catch (DecodeException e) {
+        e.printStackTrace();
+    }
+}
+</programlisting>
+
+  <para>OpenDJ directory server supports the virtual list view controls.
+  In order to set up OpenDJ directory server to produce the following output
+  with the example code, use OpenDJ Control Panel &gt; Manage Indexes &gt; New
+  VLV Index... to set up a virtual list view index for people by last name,
+  using the filter <literal>(|(givenName=*)(sn=*))</literal>, and sorting first
+  by surname, <literal>sn</literal>, in ascending order, then by given name
+  also in ascending order.</para>
+
+  <programlisting language="ldif">dn: uid=skellehe,ou=People,dc=example,dc=com
+givenName: Sue
+sn: Kelleher
+
+dn: uid=ejohnson,ou=People,dc=example,dc=com
+givenName: Emanuel
+sn: Johnson
+
+dn: uid=ajensen,ou=People,dc=example,dc=com
+givenName: Allison
+sn: Jensen
+
+dn: uid=bjense2,ou=People,dc=example,dc=com
+givenName: Bjorn
+sn: Jensen
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+givenName: Barbara
+sn: Jensen
+
+# Entries are sorted.
+# Position in list: 92/150</programlisting>
  </section>
 
  <section xml:id="custom-control">
diff --git a/opendj3/src/site/resources/Example.ldif b/opendj3/src/site/resources/Example.ldif
index 71bc402..578e04b 100644
--- a/opendj3/src/site/resources/Example.ldif
+++ b/opendj3/src/site/resources/Example.ldif
@@ -25,13 +25,6 @@
 #
 # dc=com sample LDIF file
 #
-# Notes:
-#   161 total entries.
-#     2 (objectclass=domain) entries (dc=example,dc=com).
-#     4 (objectclass=organizationalunit) entries.
-#     5 (objectclass=groupofuniquenames) entries.
-#   150 (objectclass=person) entries (all under ou=people,dc=example,dc=com).
-#
 # Schema definition for use with Class of Service collective attributes:
 #
 #dn: cn=schema
@@ -42,10 +35,24 @@
 # SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE USAGE user
 # Applications X-ORIGIN 'OpenDJ Documentation Examples' )
 #-
+#add: attributeTypes
+#attributeTypes: ( example-class-of-service-disk-quota NAME 'diskQuota
+# ' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
+# IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE user
+# Applications X-ORIGIN 'OpenDJ Documentation Examples' )
+#-
+#add: attributeTypes
+#attributeTypes: ( example-class-of-service-mail-quota NAME 'mailQuota
+# ' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR case
+# IgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE user
+# Applications X-ORIGIN 'OpenDJ Documentation Examples' )
+#-
 #add: objectClasses
 #objectClasses: ( example-class-of-service-object-class NAME 'cos' SUP top AUX
-# ILIARY MAY classOfService X-ORIGIN 'OpenDJ Documentation Examples' )
+# ILIARY MAY classOfService $ diskQuota $ mailQuota X-ORIGIN 'OpenDJ Doc
+# umentation Examples' )
 #
+
 dn: dc=com
 objectClass: domain
 objectClass: top
@@ -3746,19 +3753,46 @@
 uidNumber: 1109
 gidNumber: 1000
 
+# Quotas by class of service
+dn: cn=Bronze Class of Service,dc=example,dc=com
+objectClass: collectiveAttributeSubentry
+objectClass: extensibleObject
+objectClass: subentry
+objectClass: top
+cn: Bronze Class of Service
+diskQuota;collective: 10 GB
+mailQuota;collective: 1 GB
+subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
+ bronze)" }
+
+dn: cn=Silver Class of Service,dc=example,dc=com
+objectClass: collectiveAttributeSubentry
+objectClass: extensibleObject
+objectClass: subentry
+objectClass: top
+cn: Silver Class of Service
+diskQuota;collective: 50 GB
+mailQuota;collective: 5 GB
+subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
+ silver)" }
+
+dn: cn=Gold Class of Service,dc=example,dc=com
+objectClass: collectiveAttributeSubentry
+objectClass: extensibleObject
+objectClass: subentry
+objectClass: top
+cn: Gold Class of Service
+diskQuota;collective: 100 GB
+mailQuota;collective: 10 GB
+subtreeSpecification: { base "ou=People", specificationFilter "(classOfService=
+ gold)" }
+
 dn: ou=Special Users,dc=example,dc=com
 objectClass: organizationalUnit
 objectClass: top
 description: Special Administrative Accounts
 ou: Special Users
 
-dn: dc=references,dc=example,dc=com
-dc: references
-objectClass: extensibleObject
-objectClass: referral
-objectClass: top
-ref: ldap:///ou=People,dc=example,dc=com
-
 dn: ou=Apps,dc=example,dc=com
 objectClass: organizationalUnit
 objectClass: top
@@ -3774,3 +3808,16 @@
 userPassword: password
 ds-privilege-name: proxied-auth
 
+# Create a new base DN, dc=ref,dc=com, before importing these entries:
+dn: dc=ref,dc=com
+objectClass: domain
+objectClass: top
+dc: ref
+
+dn: dc=references,dc=ref,dc=com
+dc: references
+objectClass: extensibleObject
+objectClass: referral
+objectClass: top
+ref: ldap:///dc=example,dc=com
+

--
Gitblit v1.10.0