From 1638dfb988360db91384e62f41f845cc39f86c9c Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 31 Jan 2014 13:39:01 +0000
Subject: [PATCH] CR-2917 fix for OPENDJ-1257: Update dev guide to reflect SDK support for Active Directory Change Notifications
---
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java | 93 +++++++++++++
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java | 176 +++++++++++++++++++++++++
opendj-sdk/src/main/docbkx/dev-guide/chap-controls.xml | 124 +++++++----------
opendj-sdk/src/site/resources/Example.ldif | 3
opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm | 6
5 files changed, 327 insertions(+), 75 deletions(-)
diff --git a/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java b/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
index 824fac8..1bae0e9 100644
--- a/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
+++ b/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java
@@ -20,7 +20,7 @@
*
* CDDL HEADER END
*
- * Copyright 2012-2013 ForgeRock AS
+ * Copyright 2012-2014 ForgeRock AS
*
*/
@@ -45,6 +45,7 @@
import org.forgerock.opendj.ldap.SearchResultReferenceIOException;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.SortKey;
+import org.forgerock.opendj.ldap.controls.ADNotificationRequestControl;
import org.forgerock.opendj.ldap.controls.AssertionRequestControl;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl;
import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl;
@@ -123,6 +124,7 @@
// Uncomment a method to run one of the examples.
+ //useADNotificationRequestControl(connection);
//useAssertionControl(connection);
useAuthorizationIdentityRequestControl(connection);
// For the EntryChangeNotificationResponseControl see
@@ -156,6 +158,95 @@
}
/**
+ * Use the <a
+ * href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms676877(v=vs.85).aspx"
+ * >Microsoft LDAP Notification control</a>
+ * to register a change notification request for a search
+ * on Microsoft Active Directory.
+ * <p/>
+ * This client binds to Active Directory as
+ * {@code cn=Administrator,cn=users,dc=example,dc=com}
+ * with password {@code password},
+ * and expects entries under {@code dc=example,dc=com}.
+ *
+ * @param connection Active connection to Active Directory server.
+ * @throws ErrorResultException Operation failed.
+ */
+ static void useADNotificationRequestControl(Connection connection)
+ throws ErrorResultException {
+
+ // --- JCite ADNotification ---
+ final String user = "cn=Administrator,cn=users,dc=example,dc=com";
+ final char[] password = "password".toCharArray();
+ connection.bind(user, password);
+
+ final String[] attributes = {"cn",
+ ADNotificationRequestControl.IS_DELETED_ATTR,
+ ADNotificationRequestControl.WHEN_CHANGED_ATTR,
+ ADNotificationRequestControl.WHEN_CREATED_ATTR};
+
+ SearchRequest request =
+ Requests.newSearchRequest("dc=example,dc=com",
+ SearchScope.WHOLE_SUBTREE, "(objectclass=*)", attributes)
+ .addControl(ADNotificationRequestControl.newControl(true));
+
+ ConnectionEntryReader reader = connection.search(request);
+
+ try {
+ while (reader.hasNext()) {
+ if (!reader.isReference()) {
+ SearchResultEntry entry = reader.readEntry(); // Updated entry
+ final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+
+ Boolean isDeleted = entry.parseAttribute(
+ ADNotificationRequestControl.IS_DELETED_ATTR
+ ).asBoolean();
+ if (isDeleted != null && isDeleted) {
+ // Handle entry deletion
+ writer.writeComment("Deleted entry: "
+ + entry.getName().toString());
+ writer.writeEntry(entry);
+ writer.flush();
+ }
+ String whenCreated = entry.parseAttribute(
+ ADNotificationRequestControl.WHEN_CREATED_ATTR)
+ .asString();
+ String whenChanged = entry.parseAttribute(
+ ADNotificationRequestControl.WHEN_CHANGED_ATTR)
+ .asString();
+ if (whenCreated != null && whenChanged != null) {
+ if (whenCreated.equals(whenChanged)) {
+ // Handle entry addition
+ writer.writeComment("Added entry: "
+ + entry.getName().toString());
+ writer.writeEntry(entry);
+ writer.flush();
+ } else {
+ // Handle entry modification
+ writer.writeComment("Modified entry: "
+ + entry.getName().toString());
+ writer.writeEntry(entry);
+ writer.flush();
+ }
+ }
+ } else {
+ reader.readReference(); // Read and ignore reference
+ }
+ }
+ } catch (final ErrorResultIOException e) {
+ System.err.println(e.getMessage());
+ System.exit(e.getCause().getResult().getResultCode().intValue());
+ } catch (final SearchResultReferenceIOException e) {
+ System.err.println("Got search reference(s): " + e.getReference()
+ .getURIs().toString());
+ } catch (final IOException e) {
+ System.err.println(e.getMessage());
+ System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
+ }
+ // --- JCite ADNotification ---
+ }
+
+ /**
* Use the LDAP assertion control to modify Babs Jensen's description if
* her entry does not have a description, yet.
*
diff --git a/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java b/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java
new file mode 100644
index 0000000..a575fe5
--- /dev/null
+++ b/opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java
@@ -0,0 +1,176 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009-2010 Sun Microsystems, Inc.
+ * Portions copyright 2011-2014 ForgeRock AS
+ */
+
+package org.forgerock.opendj.examples;
+
+import org.forgerock.opendj.io.ASN1;
+import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.ldap.ByteStringBuilder;
+import org.forgerock.opendj.ldap.Connection;
+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.LDAPConnectionFactory;
+import org.forgerock.opendj.ldap.ModificationType;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.SearchScope;
+import org.forgerock.opendj.ldap.controls.GenericControl;
+import org.forgerock.opendj.ldap.controls.PreReadResponseControl;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.opendj.ldif.LDIFEntryWriter;
+
+import java.io.IOException;
+
+/**
+ * An example client application which uses
+ * {@link org.forgerock.opendj.ldap.controls.GenericControl} to pass the
+ * pre-read request control from <a href="http://tools.ietf.org/html/rfc4527"
+ * >RFC 4527 - Lightweight Directory Access Protocol (LDAP) Read Entry Controls</a>.
+ *
+ * <p>This example takes the following command line parameters:
+ *
+ * <pre>
+ * <host> <port> <username> <password> <userDN>
+ * </pre>
+ *
+ * <p>This example modifies the description attribute of an entry that
+ * you specify in the <userDN> command line parameter.
+ */
+public final class UseGenericControl {
+ /**
+ * Main method.
+ *
+ * @param args The command line arguments: host, port, username, password,
+ * base DN, where the base DN is the root of a naming context.
+ */
+ public static void main(final String[] args) {
+ if (args.length < 5) {
+ System.err.println("Usage: host port username password userDN");
+ System.exit(1);
+ }
+
+ // Parse command line arguments.
+ final String hostName = args[0];
+ final int port = Integer.parseInt(args[1]);
+ final String userName = args[2];
+ final String password = args[3];
+ final String userDN = args[4];
+
+ // --- JCite ---
+ // Create an LDIF writer to write entries to stdout.
+ final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
+
+ // Connect and bind to the server.
+ final LDAPConnectionFactory factory =
+ new LDAPConnectionFactory(hostName, port);
+ Connection connection = null;
+
+ // Prepare the value for the GenericControl.
+
+ // http://tools.ietf.org/html/rfc4527#section-3.1 says:
+ // "The Pre-Read request control is a LDAP Control [RFC4511] whose
+ // controlType is 1.3.6.1.1.13.1 and whose controlValue is a BER-encoded
+ // AttributeSelection [RFC4511], as extended by [RFC3673]."
+
+ ByteStringBuilder builder = new ByteStringBuilder();
+ ASN1Writer asn1Writer = ASN1.getWriter(builder);
+ try {
+ asn1Writer.writeStartSequence();
+ asn1Writer.writeOctetString("description");
+ asn1Writer.writeEndSequence();
+ asn1Writer.flush();
+ asn1Writer.close();
+ } catch (Exception e) {
+ System.out.println("Failed to prepare control value: "
+ + e.getCause());
+ System.exit(-1);
+ }
+
+ try {
+ connection = factory.getConnection();
+ connection.bind(userName, password.toCharArray());
+
+ // Modify the user description.
+ final ModifyRequest request =
+ Requests
+ .newModifyRequest(userDN)
+ .addModification(ModificationType.REPLACE,
+ "description", "A new description")
+ .addControl(
+ GenericControl
+ .newControl(
+ "1.3.6.1.1.13.1",
+ true,
+ builder.toByteString()));
+ final Result result = connection.modify(request);
+
+ // Display the description before and after the modification.
+ if (result.isSuccess()) {
+ final PreReadResponseControl control = result.getControl(
+ PreReadResponseControl.DECODER, new DecodeOptions()
+ );
+ final Entry unmodifiedEntry = control.getEntry();
+ writer.writeComment("Before modification");
+ writer.writeEntry(unmodifiedEntry);
+ writer.flush();
+
+ final SearchResultEntry modifiedEntry =
+ connection.searchSingleEntry(
+ userDN,
+ SearchScope.BASE_OBJECT,
+ "(objectclass=*)",
+ "description");
+ writer.writeComment("After modification");
+ writer.writeEntry(modifiedEntry);
+ writer.flush();
+ }
+
+ } catch (final ErrorResultException e) {
+ System.err.println(e.getMessage());
+ System.exit(e.getResult().getResultCode().intValue());
+ } catch (final ErrorResultIOException e) {
+ System.err.println(e.getMessage());
+ System.exit(e.getCause().getResult().getResultCode().intValue());
+ } catch (final IOException e) {
+ System.err.println(e.getMessage());
+ System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ // --- JCite ---
+ }
+
+ private UseGenericControl() {
+ // Not used.
+ }
+}
diff --git a/opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm b/opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
index ab21b51..e4145f8 100644
--- a/opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
+++ b/opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
@@ -20,7 +20,7 @@
!
! CCPL HEADER END
!
- ! Copyright 2011-2013 ForgeRock AS
+ ! Copyright 2011-2014 ForgeRock AS
!
-->
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -103,6 +103,10 @@
- illustrates how to add or remove a member from a static group
</li>
<li>
+ <a href="xref/org/forgerock/opendj/examples/UseGenericControl.html">Use <code>GenericControl</code></a>
+ - illustrates how to use <code>GenericControl</code> to add a pre-read request control
+ </li>
+ <li>
<a href="xref/org/forgerock/opendj/examples/GetADChangeNotifications.html">Use <code>GenericControl</code></a>
- illustrates how to use <code>GenericControl</code> to get change notifications from Active Directory
</li>
diff --git a/opendj-sdk/src/main/docbkx/dev-guide/chap-controls.xml b/opendj-sdk/src/main/docbkx/dev-guide/chap-controls.xml
index a3d8b62..5aed0fe 100644
--- a/opendj-sdk/src/main/docbkx/dev-guide/chap-controls.xml
+++ b/opendj-sdk/src/main/docbkx/dev-guide/chap-controls.xml
@@ -20,15 +20,15 @@
!
! CCPL HEADER END
!
- ! Copyright 2011-2013 ForgeRock AS
+ ! Copyright 2011-2014 ForgeRock AS
!
-->
<chapter xml:id='chap-controls'
xmlns='http://docbook.org/ns/docbook' version='5.0' xml:lang='en'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
- xsi:schemaLocation='http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd'
- xmlns:xlink='http://www.w3.org/1999/xlink'
- xmlns:xinclude='http://www.w3.org/2001/XInclude'>
+ xsi:schemaLocation='http://docbook.org/ns/docbook
+ http://docbook.org/xml/5.0/xsd/docbook.xsd'
+ xmlns:xlink='http://www.w3.org/1999/xlink'>
<title>Working With Controls</title>
<indexterm>
<primary>Controls</primary>
@@ -350,6 +350,33 @@
</programlisting>
</section>
+ <section xml:id="use-ad-notification-request-control">
+ <title>Microsoft LDAP Server Notification Control</title>
+ <indexterm>
+ <primary>Controls</primary>
+ <secondary>Microsoft LDAP Server Notification Control</secondary>
+ </indexterm>
+
+ <para>
+ The Microsoft <link xlink:show="new"
+ xlink:href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366983(v=vs.85).aspx"
+ >LDAP Server Notification Control</link>
+ with OID <literal>1.2.840.113556.1.4.528</literal>
+ can be used to register a change notification request
+ for a search on Microsoft Active Directory.
+ </para>
+
+ <programlisting language="java"
+ >[jcp:org.forgerock.opendj.examples.Controls:--- JCite ADNotification ---]</programlisting>
+
+ <para>
+ When you run the search against Active Directory
+ and then create, update, and delete a new user
+ Active Directory notifies you of changes to directory data.
+ </para>
+
+ </section>
+
<section xml:id="use-password-expired-control">
<title>Password Expired Response Control</title>
<indexterm>
@@ -837,79 +864,32 @@
<literal>GenericControl</literal> class when adding the control to your
request.</para>
- <para>For example, the Microsoft <link xlink:show="new"
- xlink:href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366983(v=vs.85).aspx"
- >LDAP Server Notification Control</link> with OID
- <literal>1.2.840.113556.1.4.528</literal> can be used to register a change
- notification request for a search on Microsoft Active Directory. You can use
- a <literal>GenericControl.newControl()</literal> static method to add the
- request control to your search.</para>
+ <para>
+ The following example uses a <literal>GenericControl</literal>
+ to add a pre-read request control when replacing the description
+ on a user's entry.
+ OpenDJ LDAP SDK already implements the pre-read request control,
+ as shown in <xref linkend="use-pre-read-control" />.
+ The example is of interest mainly because it shows
+ that the values that you pass when using a <literal>GenericControl</literal>
+ must be prepared as indicated in the specification of the control.
+ </para>
<programlisting language="java"
- >[jcp:org.forgerock.opendj.examples.GetADChangeNotifications:--- JCite ---]</programlisting>
+ >[jcp:org.forgerock.opendj.examples.UseGenericControl:--- JCite ---]</programlisting>
- <para>When you run the search against Active Directory and then create,
- update, and delete a new user, in this example
- <literal>CN=New User,CN=Users,DC=ad,DC=example,DC=com</literal>, Active
- Directory notifies you of changes to directory data.</para>
+ <para>
+ When you run this example against a user entry in OpenDJ directory server,
+ you see something like the following result.
+ </para>
- <programlisting language="ldif"
- ># Search result entry: CN=RID Set,CN=WIN2008R2641,OU=Domain Controllers,
- DC=ad,DC=example,DC=com
-dn: CN=RID Set,CN=WIN2008R2641,OU=Domain Controllers,DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: rIDSet
-objectGUID:: 178zQQic3EOoBOB1j2QVgQ==
-uSNChanged: 12446
+ <programlisting language="ldif"># Before modification
+dn: uid=bjensen,ou=People,dc=example,dc=com
+description: Original description
-# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: user
-objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
-uSNChanged: 12753
-
-# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: user
-objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
-uSNChanged: 12755
-
-# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: user
-objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
-uSNChanged: 12757
-
-# Search result entry: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-dn: CN=New User,CN=Users,DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: user
-objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
-uSNChanged: 12758
-
-# Search result entry: CN=New User\0ADEL:3a3f71ed-5d82-4a14-807a-0c008b678d94,
-# CN=Deleted Objects,DC=ad,DC=example,DC=com
-dn: CN=New User\0ADEL:3a3f71ed-5d82-4a14-807a-0c008b678d94,CN=Deleted Objects,
- DC=ad,DC=example,DC=com
-objectClass: top
-objectClass: person
-objectClass: organizationalPerson
-objectClass: user
-objectGUID:: 7XE/OoJdFEqAegwAi2eNlA==
-isDeleted: TRUE
-uSNChanged: 12759
-</programlisting>
+# After modification
+dn: uid=bjensen,ou=People,dc=example,dc=com
+description: A new description</programlisting>
<para>The <literal>GenericControl</literal> class is useful with controls that
do not require you to encode complex request values, or decode complex
diff --git a/opendj-sdk/src/site/resources/Example.ldif b/opendj-sdk/src/site/resources/Example.ldif
index cbac340..0c459b7 100644
--- a/opendj-sdk/src/site/resources/Example.ldif
+++ b/opendj-sdk/src/site/resources/Example.ldif
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
# Copyright 2006-2008 Sun Microsystems, Inc.
-# Portions Copyright 2012-2013 ForgeRock AS
+# Portions Copyright 2012-2014 ForgeRock AS
#
#
# dc=com sample LDIF file
@@ -682,6 +682,7 @@
preferredLanguage: en, ko;q=0.8
uidNumber: 1076
gidNumber: 1000
+description: Original description
dn: uid=bmaddox,ou=People,dc=example,dc=com
objectClass: person
--
Gitblit v1.10.0