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

Mark Craig
12.04.2015 41a1efcbe052305b1d542e74bd86756a8a953c37
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/GetADChangeNotifications.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -55,7 +55,7 @@
 * <p>This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password> &lt;baseDN>
 *  {@code <host> <port> <username> <password> <baseDN>}
 * </pre>
 *
 * <p>The {@code baseDN} must be the root of a naming context in this example.
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Modify.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -47,7 +47,7 @@
 * parameters (it will read from stdin if no LDIF file is provided):
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password> [&lt;ldifFile>]
 *  {@code <host> <port> <username> <password> [<ldifFile>]}
 * </pre>
 */
public final class Modify {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ModifyAsync.java
New file
@@ -0,0 +1,177 @@
/*
 * 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 2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * An example client application which applies update operations to a directory server
 * using the asynchronous APIs.
 * The update operations are read from an LDIF file, or stdin if no filename is provided.
 * This example takes the following command line parameters,
 * reading from stdin if no LDIF file is provided:
 *
 * <pre>
 *  {@code <host> <port> <username> <password> [<ldifFile>]}
 * </pre>
 */
public final class ModifyAsync {
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Result for the modify operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, username, password,
     *            LDIF file name containing the update operations.
     *            Stdin is used if no LDIF file name is provided.
     */
    public static void main(final String[] args) {
        if (args.length < 4 || args.length > 5) {
            System.err.println("Usage: host port username password [ldifFileName]");
            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 char[] password = args[3].toCharArray();
        // Create the LDIF reader using either the named file, if provided, or stdin.
        InputStream ldif;
        if (args.length > 4) {
            try {
                ldif = new FileInputStream(args[4]);
            } catch (final FileNotFoundException e) {
                System.err.println(e.getMessage());
                System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
                return;
            }
        } else {
            ldif = System.in;
        }
        final String[] ldifLines = getInputLines(ldif);
        // Connect to the server, bind, and request the modifications.
        new LDAPConnectionFactory(hostName, port)
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(Connection connection)
                            throws LdapException {
                        ModifyAsync.connection = connection;
                        return connection.bindAsync(
                                Requests.newSimpleBindRequest(userName, password));
                    }
                })
                .thenAsync(new AsyncFunction<BindResult, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(BindResult bindResult)
                            throws LdapException {
                        return connection.modifyAsync(
                                Requests.newModifyRequest(ldifLines));
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        resultCode = result.getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    /**
     * Returns the lines from the input stream.
     * @param in    The input stream.
     * @return The lines from the input stream.
     */
    private static String[] getInputLines(final InputStream in) {
        String line;
        final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        final List<String> lines = new ArrayList<>();
        try {
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
        }
        return lines.toArray(new String[lines.size()]);
    }
    private ModifyAsync() {
        // Not used.
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/PasswordResetForAD.java
@@ -20,7 +20,7 @@
 *
 * CDDL HEADER END
 *
 *      Copyright 2013-2014 ForgeRock AS
 *      Copyright 2013-2015 ForgeRock AS.
 *
 */
@@ -45,7 +45,7 @@
/**
 * This command-line client demonstrates how to reset a user password in
 * Microsoft Active Directory.
 * <p>
 * <br>
 * The client takes as arguments the host and port of the Active Directory
 * server, a flag indicating whether this is a self-reset (user changing own
 * password) or an administrative reset (administrator changing a password),
@@ -56,7 +56,7 @@
    /**
     * Reset a user password in Microsoft Active Directory.
     * <p>
     * <br>
     * The connection should be LDAPS, not LDAP, in order to perform the
     * modification.
     *
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Proxy.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -57,8 +57,8 @@
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;listenAddress> &lt;listenPort> &lt;proxyDN> &ltproxyPassword> &lt;remoteAddress1> &lt;remotePort1>
 *      [&lt;remoteAddress2> &lt;remotePort2> ...]
 *     {@code <listenAddress> <listenPort> <proxyDN> <proxyPassword> <remoteAddress1> <remotePort1>
 *      [<remoteAddress2> <remotePort2> ...]}
 * </pre>
 */
public final class Proxy {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ProxyBackend.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -78,15 +78,16 @@
 * client connection. The following code illustrates how this may be achieved:
 *
 * <pre>
 * final RequestHandlerFactory&lt;LDAPClientContext, RequestContext&gt; proxyFactory =
 *     new RequestHandlerFactory&lt;LDAPClientContext, RequestContext&gt;() {
 *         &#064;Override
 *     {@code
 * final RequestHandlerFactory<LDAPClientContext, RequestContext> proxyFactory =
 *     new RequestHandlerFactory<LDAPClientContext, RequestContext>() {
 *         @Override
 *         public ProxyBackend handleAccept(LDAPClientContext clientContext) throws LdapException {
 *             return new ProxyBackend(factory, bindFactory);
 *         }
 *     };
 * final ServerConnectionFactory&lt;LDAPClientContext, Integer&gt; connectionHandler = Connections
 *     .newServerConnectionFactory(proxyFactory);
 * final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler = Connections
 *     .newServerConnectionFactory(proxyFactory);}
 * </pre>
 */
final class ProxyBackend implements RequestHandler<RequestContext> {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ReadSchema.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -44,7 +44,7 @@
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 *  {@code <host> <port> <username> <password>}
 * </pre>
 */
public final class ReadSchema {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/RewriterProxy.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -98,7 +98,7 @@
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;localAddress> &lt;localPort> &lt;proxyDN> &lt;proxyPassword> &lt;serverAddress> &lt;serverPort>
 *  {@code <localAddress> <localPort> <proxyDN> <proxyPassword> <serverAddress> <serverPort>}
 * </pre>
 *
 * If you have imported the users from <a
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SASLAuth.java
@@ -21,7 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2011-2014 ForgeRock AS
 *      Copyright 2011-2015 ForgeRock AS.
 */
/**
@@ -52,7 +52,7 @@
 * command line parameters:
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server for StartTLS, e.g. 1389</li>
 * <li>port - port number of the directory server for StartTLS</li>
 * <li>authzid - (Optional) Authorization identity</li>
 * <li>authcid - Authentication identity</li>
 * <li>passwd - Password of the user to authenticate</li>
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Search.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -45,8 +45,8 @@
 * takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 *  {@code <host> <port> <username> <password>
 *      <baseDN> <scope> <filter> [<attribute> <attribute> ...]}
 * </pre>
 */
public final class Search {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchAsync.java
@@ -21,8 +21,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -55,8 +54,8 @@
 * asynchronous APIs. This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 *  {@code <host> <port> <username> <password>
 *      <baseDN> <scope> <filter> [<attribute> <attribute> ...]}
 * </pre>
 */
public final class SearchAsync {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchBind.java
@@ -20,7 +20,7 @@
 *
 * CDDL HEADER END
 *
 *      Copyright 2012-2014 ForgeRock AS
 *      Copyright 2012-2015 ForgeRock AS.
 *
 */
@@ -44,7 +44,7 @@
 * entry.
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server, e.g. 1389, 1636</li>
 * <li>port - port number of the directory server</li>
 * <li>base-dn - base DN for the search, e.g. dc=example,dc=com</li>
 * </ul>
 * All arguments are required.
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SearchBindAsync.java
New file
@@ -0,0 +1,183 @@
/*
 * 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 2015 ForgeRock AS.
 *
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.CountDownLatch;
/**
 * An interactive command-line client that performs a search and simple bind
 * using the asynchronous APIs.
 * <br>
 * The client prompts for email address and for a password,
 * and then searches based on the email address,
 * to bind as the user with the password.
 * <br>
 * If successful, the client displays the common name from the user's entry.
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server</li>
 * <li>base-dn - base DN for the search, e.g. dc=example,dc=com</li>
 * </ul>
 * All arguments are required.
 */
public final class SearchBindAsync {
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Email address provided by user. */
    private static String mail;
    /** Password provided by user. */
    private static char[] password;
    /** Bind DN returned by the search. */
    private static String bindDn;
    /** Result for the operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Prompts for email and password, search and bind, then display message.
     *
     * @param args
     *            The command line arguments: host, port, base-dn.
     */
    public static void main(final String[] args) {
        if (args.length != 3) {
            System.err.println("Usage: host port base-dn");
            System.err.println("For example: localhost 1389 dc=example,dc=com");
            System.exit(1);
        }
        final String host   = args[0];
        final int    port   = Integer.parseInt(args[1]);
        final String baseDn = args[2];
        // Prompt for email address and password.
        try {
            mail = getInputLine("Email address:");
            password = getInputLine("Password:").toCharArray();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
            return;
        }
        // Connect to the server, search for the user entry based on email address, and bind.
        new LDAPConnectionFactory(host, port)
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, SearchResultEntry, LdapException>() {
                    @Override
                    public Promise<SearchResultEntry, LdapException> apply(Connection connection)
                            throws LdapException {
                        SearchBindAsync.connection = connection;
                        return connection.searchSingleEntryAsync(
                                Requests.newSingleEntrySearchRequest(
                                        baseDn,
                                        SearchScope.WHOLE_SUBTREE,
                                        Filter.equality("mail", mail).toString(),
                                        "cn"));
                    }
                })
                .thenAsync(new AsyncFunction<SearchResultEntry, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(SearchResultEntry searchResultEntry)
                            throws LdapException {
                        SearchBindAsync.bindDn = searchResultEntry.getName().toString();
                        return SearchBindAsync.connection.bindAsync(
                                Requests.newSimpleBindRequest(bindDn, password));
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        if (result.getResultCode() == ResultCode.SUCCESS) {
                            System.out.println("Authenticated as " + SearchBindAsync.bindDn + ".");
                        }
                        resultCode = result.getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        }  catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    /**
     * Returns an input string from System.in, after prompting on System.out.
     * <br>
     * Note: The input is echoed to the display.
     *
     * @param prompt    The prompt asking for input.
     * @return An input string from System.in.
     * @throws IOException Failed to read from System.in.
     */
    private static String getInputLine(final String prompt) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        System.out.print(prompt + " ");
        return reader.readLine();
    }
    /**
     * Constructor not used.
     */
    private SearchBindAsync() {
        // Not used
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/Server.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -57,7 +57,7 @@
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;listenAddress> &lt;listenPort> &lt;ldifFile> [&lt;keyStoreFile> &lt;keyStorePassword> &lt;certNickname>]
 *  {@code <listenAddress> <listenPort> <ldifFile> [<keyStoreFile> <keyStorePassword> <certNickname>]}
 * </pre>
 */
public final class Server {
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ShortLife.java
@@ -20,7 +20,7 @@
 *
 * CDDL HEADER END
 *
 *      Copyright 2012-2014 ForgeRock AS
 *      Copyright 2012-2015 ForgeRock AS.
 *
 */
@@ -49,7 +49,7 @@
 *
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server, e.g. 1389, 1636</li>
 * <li>port - port number of the directory server</li>
 * </ul>
 * All arguments are required.
 */
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/ShortLifeAsync.java
New file
@@ -0,0 +1,227 @@
/*
 * 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 2015 ForgeRock AS.
 *
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.Entries;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.LinkedHashMapEntry;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.TreeMapEntry;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldif.LDIFEntryWriter;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
 * A command-line client that creates, updates, renames, and deletes a
 * short-lived entry in order to demonstrate LDAP write operations
 * using the asynchronous APIs.
 * <br>
 * The client takes as arguments the host and port for the directory server,
 * and expects to find the entries and access control instructions as defined in
 * <a href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>.
 *
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server</li>
 * </ul>
 *
 * All arguments are required.
 */
public final class ShortLifeAsync {
    /** The short-lived entry. */
    private static Entry entry;
    /** Writer for displaying LDIF to System.out. */
    private static LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Result for the operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Adds, modifies, renames, and deletes an entry.
     *
     * @param args
     *            The command line arguments: host, port
     */
    public static void main(final String[] args) {
        if (args.length != 2) {
            System.err.println("Usage: host port");
            System.err.println("For example: localhost 1389");
            System.exit(1);
        }
        final String host = args[0];
        final int    port = Integer.parseInt(args[1]);
        // User credentials of a "Directory Administrators" group member.
        // Kirsten Vaughan is authorized to create, update, and delete entries.
        //
        // Alternatively, prompt an administrator user for credentials,
        // or get the application its own account with access to update data.
        final String adminDn  = "uid=kvaughan,ou=people,dc=example,dc=com";
        final char[] adminPwd = "bribery".toCharArray();
        // Prepare an entry to add to the directory.
        final String entryDn = "cn=Bob,ou=People,dc=example,dc=com";
        entry = new LinkedHashMapEntry(entryDn)
            .addAttribute("cn", "Bob")
            .addAttribute("objectclass", "top")
            .addAttribute("objectclass", "person")
            .addAttribute("objectclass", "organizationalPerson")
            .addAttribute("objectclass", "inetOrgPerson")
            .addAttribute("mail", "subgenius@example.com")
            .addAttribute("sn", "Dobbs");
        new LDAPConnectionFactory(host, port)
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(Connection connection)
                            throws LdapException {
                        ShortLifeAsync.connection = connection;
                        return connection.bindAsync(
                                Requests.newSimpleBindRequest(adminDn, adminPwd));
                    }
                })
                .thenAsync(new AsyncFunction<BindResult, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(BindResult bindResult)
                            throws LdapException {
                        log("Adding the entry...");
                        log(entry);
                        return connection.addAsync(Requests.newAddRequest(entry));
                    }
                })
                .thenAsync(new AsyncFunction<Result, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(Result result)
                            throws LdapException {
                        Entry old = TreeMapEntry.deepCopyOfEntry(entry);
                        entry = entry
                                .replaceAttribute("mail", "spammer@example.com")
                                .addAttribute("description", "Good user gone bad");
                        log("Updating mail address, adding description...");
                        log(entry);
                        ModifyRequest request = Entries.diffEntries(old, entry);
                        return connection.modifyAsync(request);
                    }
                })
                .thenAsync(new AsyncFunction<Result, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(Result result)
                            throws LdapException {
                        entry = entry.setName("cn=Renamed,ou=People,dc=example,dc=com");
                        log("Renaming the entry...");
                        log(entry);
                        return connection.modifyDNAsync(
                                Requests.newModifyDNRequest(entryDn, "cn=Renamed"));
                    }
                })
                .thenAsync(new AsyncFunction<Result, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(Result result)
                            throws LdapException {
                        final String newDn = entryDn.replace("Bob", "Renamed");
                        log("Deleting " + newDn + "...");
                        return connection.deleteAsync(
                                Requests.newDeleteRequest(newDn));
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        resultCode = result.getResultCode().intValue();
                        log("... done.");
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        }  catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    /**
     * Log a message to System.out.
     *
     * @param message   The message to write to the console.
     */
    private static void log(final String message) {
        System.out.println(message);
    }
    /**
     * Log an entry in LDIF form.
     *
     * @param entry     The entry to log.
     */
    private static void log(final Entry entry) {
        try {
            writer.writeEntry(entry);
            writer.flush();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
        }
    }
    /**
     * Constructor not used.
     */
    private ShortLifeAsync() {
        // Not used.
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/SimpleAuthAsync.java
New file
@@ -0,0 +1,268 @@
/*
 * 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 2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.TrustManagers;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.ResultHandler;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.File;
import java.security.GeneralSecurityException;
import java.util.concurrent.CountDownLatch;
/**
 * An example client application which performs simple authentication to a
 * directory server using the asynchronous APIs.
 * <br>
 * This example takes the following command line parameters:
 * <ul>
 * <li>host - host name of the directory server</li>
 * <li>port - port number of the directory server</li>
 * <li>bind-dn - DN of the user to authenticate</li>
 * <li>bind-password - Password of the user to authenticate</li>
 * <li>use-starttls - (Optional) connect with StartTLS</li>
 * <li>use-ssl - (Optional) connect over SSL</li>
 * </ul>
 * The host, port, bind-dn, and bind-password arguments are required.
 * The use-starttls and use-ssl arguments are optional and mutually exclusive.
 * <br>
 * If the server certificate is self-signed,
 * or otherwise not trusted out-of-the-box,
 * then set the trust store by using the JSSE system property
 * {@code -Djavax.net.ssl.trustStore=/path/to/opendj/config/keystore}
 * and the trust store password if necessary by using the JSSE system property
 * {@code -Djavax.net.ssl.trustStorePassword=`cat /path/to/opendj/config/keystore.pin`}.
 */
public final class SimpleAuthAsync {
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Result for the modify operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Authenticate to the directory either over LDAP, over LDAPS, or using
     * StartTLS.
     *
     * @param args
     *            The command line arguments
     */
    public static void main(final String[] args) {
        parseArgs(args);
        // Connect and bind.
        // Pass getTrustAllOptions() instead of getTrustOptions()
        // to the connection factory constructor
        // if you want to trust all certificates blindly.
        new LDAPConnectionFactory(host, port, getTrustOptions(host, keystore, storepass))
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(Connection connection)
                            throws LdapException {
                        SimpleAuthAsync.connection = connection;
                        return connection.bindAsync(
                                Requests.newSimpleBindRequest(bindDN, bindPassword.toCharArray()));
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        resultCode = result.getResultCode().intValue();
                        System.out.println("Authenticated as " + bindDN + ".");
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        }  catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    /**
     * For StartTLS and SSL the connection factory needs SSL context options.
     * In the general case, a trust manager in the SSL context serves
     * to check server certificates, and a key manager handles client keys
     * when the server checks certificates from our client.
     * <br>
     * This sample checks the server certificate,
     * verifying that the certificate is currently valid,
     * and that the host name of the server matches that of the certificate,
     * based on a Java Key Store-format trust store.
     * This sample does not present a client certificate.
     *
     * @param hostname      Host name expected in the server certificate
     * @param truststore    Path to trust store file for the trust manager
     * @param storepass     Password for the trust store
     * @return SSL context options if SSL or StartTLS is used.
     */
    private static LDAPOptions getTrustOptions(final String hostname,
                                               final String truststore,
                                               final String storepass) {
        LDAPOptions lo = new LDAPOptions();
        if (useSSL || useStartTLS) {
            TrustManager trustManager = null;
            try {
                trustManager = TrustManagers.checkValidityDates(
                        TrustManagers.checkHostName(hostname,
                                TrustManagers.checkUsingTrustStore(
                                        truststore, storepass.toCharArray(), null)));
                if (trustManager != null) {
                    SSLContext sslContext = new SSLContextBuilder()
                            .setTrustManager(trustManager).getSSLContext();
                    lo.setSSLContext(sslContext);
                }
            } catch (Exception e) {
                System.err.println(e.getMessage());
                System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
            }
            lo.setUseStartTLS(useStartTLS);
        }
        return lo;
    }
    /**
     * For StartTLS and SSL the connection factory needs SSL context options. In
     * the general case, a trust manager in the SSL context serves to check
     * server certificates, and a key manager handles client keys when the
     * server checks certificates from our client.
     * <br>
     * OpenDJ directory server lets you install by default with a self-signed
     * certificate that is not in the system trust store. To simplify this
     * implementation trusts all server certificates.
     *
     * @return SSL context options to trust all certificates without checking.
     */
    private static LDAPOptions getTrustAllOptions() {
        LDAPOptions lo = new LDAPOptions();
        try {
            SSLContext sslContext =
                    new SSLContextBuilder().setTrustManager(TrustManagers.trustAll())
                            .getSSLContext();
            lo.setSSLContext(sslContext);
            lo.setUseStartTLS(useStartTLS);
        } catch (GeneralSecurityException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
        }
        return lo;
    }
    private static String  host;
    private static int     port;
    private static String  bindDN;
    private static String  bindPassword;
    private static boolean useStartTLS;
    private static boolean useSSL;
    private static String  keystore;
    private static String  storepass;
    /**
     * Parse command line arguments.
     *
     * @param args
     *            host port bind-dn bind-password [ use-starttls | use-ssl ]
     */
    private static void parseArgs(String[] args) {
        if (args.length < 4 || args.length > 5) {
            giveUp();
        }
        host = args[0];
        port = Integer.parseInt(args[1]);
        bindDN = args[2];
        bindPassword = args[3];
        if (args.length == 5) {
            if ("use-starttls".equals(args[4].toLowerCase())) {
                useStartTLS = true;
                useSSL = false;
            } else if ("use-ssl".equals(args[4].toLowerCase())) {
                useStartTLS = false;
                useSSL = true;
            } else {
                giveUp();
            }
        }
        keystore = System.getProperty("javax.net.ssl.trustStore");
        storepass = System.getProperty("javax.net.ssl.trustStorePassword");
        if (keystore == null) { // Try to use Java's cacerts trust store.
            keystore = System.getProperty("java.home") + File.separator
                    + "lib" + File.separator
                    + "security" + File.separator
                    + "cacerts";
            storepass = "changeit"; // Default password
        }
    }
    private static void giveUp() {
        printUsage();
        System.exit(1);
    }
    private static void printUsage() {
        System.err.println("Usage: host port bind-dn bind-password [ use-starttls | use-ssl ]");
        System.err.println("\thost, port, bind-dn, and bind-password arguments are required.");
        System.err.println("\tuse-starttls and use-ssl are optional and mutually exclusive.");
        System.err.println("\tOptionally set javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword.");
    }
    private SimpleAuthAsync() {
        // Not used.
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UpdateGroupAsync.java
New file
@@ -0,0 +1,233 @@
/*
 * 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 2015 ForgeRock AS.
 *
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.RootDSE;
import org.forgerock.opendj.ldap.controls.PermissiveModifyRequestControl;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.Promises;
import org.forgerock.util.promise.ResultHandler;
import java.util.concurrent.CountDownLatch;
/**
 * This command-line client demonstrates adding and removing a member from a
 * (potentially large) static group using the asynchronous APIs.
 *
 * The client takes as arguments the host and port of the directory server, the
 * group DN, the member DN, and whether to "add" or "del" the specified member
 * from the group. The client uses the Permissive Modify control if it is
 * available to avoid having to check whether the member belongs to the group or
 * not.
 *
 * This client expects a group that is a <code>groupOfNames</code> such as:
 *
 * <pre>
 * dn: cn=My Static Group,ou=Groups,dc=example,dc=com
 * cn: My Static Group
 * objectClass: groupOfNames
 * objectClass: top
 * ou: Groups
 * member: uid=ahunter,ou=People,dc=example,dc=com
 * member: uid=bjensen,ou=People,dc=example,dc=com
 * member: uid=tmorris,ou=People,dc=example,dc=com
 * </pre>
 *
 * This client connects as <code>cn=Directory Manager</code> with password
 * <code>password</code>. Not a best practice; in real code use application
 * specific credentials to connect, and ensure that your application has access
 * to use the Permissive Modify control if your directory server supports it.
 */
public final class UpdateGroupAsync {
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Result for the operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Updates the group as necessary.
     *
     * @param args
     *            The command line arguments: host, port, group-dn, member-dn, (add|del)
     */
    public static void main(String[] args) {
        if (args.length != 5) {
            printUsage();
        }
        final String host              = args[0];
        final int port                 = Integer.parseInt(args[1]);
        final String groupDn           = args[2];
        final String memberDn          = args[3];
        final ModificationType modType = getModificationType(args[4]);
        // Connect, bind, update group.
        new LDAPConnectionFactory(host, port)
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(Connection connection)
                            throws LdapException {
                        UpdateGroupAsync.connection = connection;
                        return connection.bindAsync(
                                Requests.newSimpleBindRequest("cn=Directory Manager", "password".toCharArray()));
                    }
                })
                .thenAsync(new AsyncFunction<BindResult, RootDSE, LdapException>() {
                    @Override
                    public Promise<RootDSE, LdapException> apply(BindResult bindResult)
                            throws LdapException {
                        return RootDSE.readRootDSEAsync(connection, null);
                    }
                })
                .thenAsync(new AsyncFunction<RootDSE, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(RootDSE rootDSE) throws LdapException {
                        // If the directory supports the Permissive Modify request control,
                        // then the modification type does not matter.
                        if (rootDSE.getSupportedControls().contains(PermissiveModifyRequestControl.OID)) {
                            log("Updating group membership.");
                            return connection.modifyAsync(
                                    Requests.newModifyRequest(groupDn)
                                            .addControl(PermissiveModifyRequestControl.newControl(true))
                                            .addModification(modType, "member", memberDn));
                        } else {
                            return connection
                                    // Check whether the member is present.
                                    .compareAsync(Requests.newCompareRequest(groupDn, "member", memberDn))
                                    .thenAsync(new AsyncFunction<CompareResult, Result, LdapException>() {
                                        @Override
                                        public Promise<Result, LdapException> apply(CompareResult compareResult)
                                                throws LdapException {
                                            ResultCode rc = compareResult.getResultCode();
                                            // Only add the member if missing from the group.
                                            if (modType.equals(ModificationType.ADD)
                                                    && rc.equals(ResultCode.COMPARE_FALSE)) {
                                                log("Adding " + memberDn + " to " + groupDn + ".");
                                                return connection.modifyAsync(
                                                        Requests.newModifyRequest(groupDn)
                                                                .addModification(modType, "member", memberDn));
                                            // Only delete if present in the group.
                                            } else if (modType.equals(ModificationType.DELETE)
                                                    && rc.equals(ResultCode.COMPARE_TRUE)) {
                                                log("Deleting " + memberDn + " from " + groupDn + ".");
                                                return connection.modifyAsync(
                                                        Requests.newModifyRequest(groupDn)
                                                                .addModification(modType, "member", memberDn));
                                            } else {
                                                return Promises.newResultPromise(
                                                        Responses.newResult(ResultCode.SUCCESS));
                                            }
                                        }
                                    });
                        }
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        final String op = (modType == ModificationType.ADD) ? "added to" : "deleted from";
                        log(memberDn + " has been " + op + " the group " + groupDn + ".");
                        resultCode = result.getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    /**
     * Print usage then exit.
     */
    private static void printUsage() {
        System.err.println("Usage: host port group-dn member-dn {add|del}");
        System.err.println("For example: localhost 1389 "
                + "cn=Static,ou=Groups,dc=example,dc=com "
                + "uid=user.5150,ou=People,dc=example,dc=com "
                + "del");
        System.exit(1);
    }
    /**
     * Return the modification type for the update operation.
     * @param operation Operation specified as an argument (add or del).
     */
    private static ModificationType getModificationType(String operation) {
        final boolean isAdd = "add".equalsIgnoreCase(operation);
        if (!isAdd && !"del".equalsIgnoreCase(operation)) {
            printUsage();
        }
        return isAdd ? ModificationType.ADD : ModificationType.DELETE;
    }
    /**
     * Log a message to System.out.
     *
     * @param message   The message to write to the console.
     */
    private static void log(final String message) {
        System.out.println(message);
    }
    /**
     * Constructor not used.
     */
    private UpdateGroupAsync() {
        // Not used.
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseGenericControl.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2014 ForgeRock AS
 *      Portions Copyright 2011-2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
@@ -54,14 +54,14 @@
 * 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:
 * <br>This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password> &lt;userDN>
 *  {@code <host> <port> <username> <password> <userDN>}
 * </pre>
 *
 * <p>This example modifies the description attribute of an entry that
 * you specify in the &lt;userDN> command line parameter.
 * <br>This example modifies the description attribute of an entry that
 * you specify in the {@code <userDN>} command line parameter.
 */
public final class UseGenericControl {
    /**
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseSchema.java
@@ -53,7 +53,7 @@
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;bindDN> &lt;bindPassword>
 *  {@code <host> <port> <bindDN> <bindPassword>}
 * </pre>
 *
 * Then it reads an entry to add from System.in.
opendj-sdk/opendj-ldap-sdk-examples/src/main/java/org/forgerock/opendj/examples/UseSchemaAsync.java
New file
@@ -0,0 +1,183 @@
/*
 * 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 2015 ForgeRock AS.
 */
package org.forgerock.opendj.examples;
import static org.forgerock.util.Utils.closeSilently;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy;
import org.forgerock.opendj.ldif.LDIFEntryReader;
import org.forgerock.util.AsyncFunction;
import org.forgerock.util.promise.ExceptionHandler;
import org.forgerock.util.promise.Promise;
import org.forgerock.util.promise.Promises;
import org.forgerock.util.promise.ResultHandler;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
 * This example command-line client application validates an entry
 * against the directory server schema before adding it
 * using the asynchronous APIs.
 *
 * <br>
 *
 * This example takes the following command line parameters:
 *
 * <pre>
 *  {@code <host> <port> <bindDN> <bindPassword>}
 * </pre>
 *
 * Then it reads an entry to add from System.in.
 * If the entry is valid according to the directory schema,
 * it tries to add the entry to the directory.
 */
public final class UseSchemaAsync {
    /** Connection to the LDAP server. */
    private static Connection connection;
    /** Result for the operation. */
    private static int resultCode;
    /** Count down latch to wait for modify operation to complete. */
    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
    /**
     * Main method.
     *
     * @param args
     *            The command line arguments: host, port, bindDN, bindPassword.
     */
    public static void main(final String[] args) {
        if (args.length != 4) {
            System.err.println("Usage: host port bindDN bindPassword");
            System.exit(1);
        }
        // Parse command line arguments.
        final String host         = args[0];
        final int    port         = Integer.parseInt(args[1]);
        final String bindDn       = args[2];
        final char[] bindPassword = args[3].toCharArray();
        // Read an entry from System.in.
        final Entry entry;
        try {
            System.out.println("Enter entry to add in LDIF format:");
            entry = new LDIFEntryReader(System.in).readEntry();
        } catch (IOException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
            return;
        }
        final String entryDn = entry.getName().toString();
        // Connect, bind, read schema, and add entry if valid according to schema.
        new LDAPConnectionFactory(host, port)
                .getConnectionAsync()
                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
                    @Override
                    public Promise<BindResult, LdapException> apply(Connection connection)
                            throws LdapException {
                        UseSchemaAsync.connection = connection;
                        return connection.bindAsync(
                                Requests.newSimpleBindRequest(bindDn, bindPassword));
                    }
                })
                .thenAsync(new AsyncFunction<BindResult, Schema, LdapException>() {
                    @Override
                    public Promise<Schema, LdapException> apply(BindResult bindResult)
                            throws LdapException {
                        return Schema.readSchemaForEntryAsync(connection, DN.rootDN());
                    }
                })
                .thenAsync(new AsyncFunction<Schema, Result, LdapException>() {
                    @Override
                    public Promise<Result, LdapException> apply(Schema schema)
                            throws LdapException {
                        final List<LocalizableMessage> schemaErrors = new LinkedList<>();
                        boolean isValid = schema.validateEntry(
                                entry,
                                SchemaValidationPolicy.defaultPolicy(),
                                schemaErrors);
                        if (isValid) {
                            System.out.println("Processing ADD request for " + entryDn);
                            return connection.addAsync(Requests.newAddRequest(entry));
                        } else {
                            for (LocalizableMessage error : schemaErrors) {
                                System.err.println(error);
                            }
                            return Promises.newExceptionPromise(
                                    LdapException.newLdapException(
                                            ResultCode.CLIENT_SIDE_PARAM_ERROR,
                                            "Entry does not conform to schema."));
                        }
                    }
                })
                .thenOnResult(new ResultHandler<Result>() {
                    @Override
                    public void handleResult(Result result) {
                        System.out.println("ADD operation successful for DN " + entryDn);
                        resultCode = result.getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                })
                .thenOnException(new ExceptionHandler<LdapException>() {
                    @Override
                    public void handleException(LdapException e) {
                        System.err.println(e.getMessage());
                        resultCode = e.getResult().getResultCode().intValue();
                        COMPLETION_LATCH.countDown();
                    }
                });
        try {
            COMPLETION_LATCH.await();
        }  catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
            return;
        }
        closeSilently(connection);
        System.exit(resultCode);
    }
    private UseSchemaAsync() {
        // Not used.
    }
}
opendj-sdk/opendj-ldap-sdk-examples/src/site/xdoc/index.xml.vm
@@ -31,7 +31,12 @@
  <body>
    <section name="About ${project.name}">
      <p>This module contains example LDAP applications implemented using the
        OpenDJ LDAP SDK:</p>
        OpenDJ LDAP SDK.</p>
      <p>
        The following examples use the synchronous APIs:
      </p>
      <ul>
        <li>
          <a href="xref/org/forgerock/opendj/examples/Search.html">LDAP search</a>
@@ -39,11 +44,6 @@
          synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SearchAsync.html">LDAP asynchronous search</a>
          - illustrates how to perform an LDAP search operation using the
          asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/Modify.html">LDAP modify</a>
          - illustrates how to perform an LDAP modify operation using the
          synchronous APIs
@@ -53,12 +53,8 @@
          - illustrates how to implement a very simple LDAP server
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/Proxy.html">LDAP proxy</a>
          - illustrates how to implement a very simple LDAP proxy
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SimpleAuth.html">LDAP bind</a>
          - illustrates how to bind to an LDAP server
          - illustrates how to bind to an LDAP server using the synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SASLAuth.html">LDAP SASL bind</a>
@@ -79,14 +75,17 @@
        <li>
          <a href="xref/org/forgerock/opendj/examples/SearchBind.html">Search &amp; bind</a>
          - illustrates how to authenticate given a mail address and a password
          using the synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/ShortLife.html">Short life</a>
          - illustrates how to create, update, rename, and delete an entry
          using the synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/UseSchema.html">Use LDAP Schema</a>
          - illustrates how to validate an entry using the directory server LDAP schema
          using the synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/Controls.html">Use LDAP Controls</a>
@@ -98,12 +97,9 @@
          operations
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/RewriterProxy.html">Rewrite proxy</a>
          - illustrates how to rewrite DNs and attribute names in a proxy layer
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/UpdateGroup.html">Update group</a>
          - illustrates how to add or remove a member from a static group
          using the synchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/UseGenericControl.html">Use <code>GenericControl</code></a>
@@ -119,6 +115,55 @@
            or change the password as the user
        </li>
      </ul>
      <p>
        The following examples use the asynchronous APIs:
      </p>
      <ul>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SearchAsync.html">LDAP search (async)</a>
          - illustrates how to perform an LDAP search operation using the
          asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/ModifyAsync.html">LDAP modify (async)</a>
          - illustrates how to perform an LDAP modify operation using the
          asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/Proxy.html">LDAP proxy</a>
          - illustrates how to implement a very simple LDAP proxy
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SimpleAuthAsync.html">LDAP bind (async)</a>
          - illustrates how to bind to an LDAP server using the asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/SearchBindAsync.html">Search &amp; bind (async)</a>
          - illustrates how to authenticate given a mail address and a password
          using the asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/ShortLifeAsync.html">Short life (async)</a>
          - illustrates how to create, update, rename, and delete an entry
          using the asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/UseSchemaAsync.html">Use LDAP Schema (async)</a>
          - illustrates how to validate an entry using the directory server LDAP schema
          using the asynchronous APIs
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/RewriterProxy.html">Rewrite proxy</a>
          - illustrates how to rewrite DNs and attribute names in a proxy layer
        </li>
        <li>
          <a href="xref/org/forgerock/opendj/examples/UpdateGroupAsync.html">Update group (async)</a>
          - illustrates how to add or remove a member from a static group
          using the asynchronous APIs
        </li>
      </ul>
    </section>
    <section name="Documentation for ${project.name}">
      <p>