mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Mark Craig
31.39.2014 5ee7a4f468264361fff1d5ff6831d396be2e5d4d
CR-2917 fix for OPENDJ-1257: Update dev guide to reflect SDK support for Active Directory Change Notifications
1 files added
4 files modified
402 ■■■■ changed files
opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Controls.java 93 ●●●●● patch | view | raw | blame | history
opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java 176 ●●●●● patch | view | raw | blame | history
opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm 6 ●●●● patch | view | raw | blame | history
src/main/docbkx/dev-guide/chap-controls.xml 124 ●●●●● patch | view | raw | blame | history
src/site/resources/Example.ldif 3 ●●●● patch | view | raw | blame | history
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.
     *
opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java
New file
@@ -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>
 *  &lt;host> &lt;port> &lt;username> &lt;password> &lt;userDN>
 * </pre>
 *
 * <p>This example modifies the description attribute of an entry that
 * you specify in the &lt;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.
    }
}
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>
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
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