001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015 ForgeRock AS.
015 */
016
017package org.forgerock.opendj.examples;
018
019import static org.forgerock.util.Utils.closeSilently;
020import org.forgerock.i18n.LocalizableMessage;
021import org.forgerock.opendj.ldap.Connection;
022import org.forgerock.opendj.ldap.DN;
023import org.forgerock.opendj.ldap.Entry;
024import org.forgerock.opendj.ldap.LDAPConnectionFactory;
025import org.forgerock.opendj.ldap.LdapException;
026import org.forgerock.opendj.ldap.ResultCode;
027import org.forgerock.opendj.ldap.requests.Requests;
028import org.forgerock.opendj.ldap.responses.BindResult;
029import org.forgerock.opendj.ldap.responses.Result;
030import org.forgerock.opendj.ldap.schema.Schema;
031import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy;
032import org.forgerock.opendj.ldif.LDIFEntryReader;
033import org.forgerock.util.AsyncFunction;
034import org.forgerock.util.promise.ExceptionHandler;
035import org.forgerock.util.promise.Promise;
036import org.forgerock.util.promise.Promises;
037import org.forgerock.util.promise.ResultHandler;
038
039import java.io.IOException;
040import java.util.LinkedList;
041import java.util.List;
042import java.util.concurrent.CountDownLatch;
043
044/**
045 * This example command-line client application validates an entry
046 * against the directory server schema before adding it
047 * using the asynchronous APIs.
048 *
049 * <br>
050 *
051 * This example takes the following command line parameters:
052 *
053 * <pre>
054 *  {@code <host> <port> <bindDN> <bindPassword>}
055 * </pre>
056 *
057 * Then it reads an entry to add from System.in.
058 * If the entry is valid according to the directory schema,
059 * it tries to add the entry to the directory.
060 */
061public final class UseSchemaAsync {
062    /** Connection to the LDAP server. */
063    private static Connection connection;
064    /** Result for the operation. */
065    private static int resultCode;
066    /** Count down latch to wait for modify operation to complete. */
067    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
068
069    /**
070     * Main method.
071     *
072     * @param args
073     *            The command line arguments: host, port, bindDN, bindPassword.
074     */
075    public static void main(final String[] args) {
076        if (args.length != 4) {
077            System.err.println("Usage: host port bindDN bindPassword");
078            System.exit(1);
079        }
080
081        // Parse command line arguments.
082        final String host         = args[0];
083        final int    port         = Integer.parseInt(args[1]);
084        final String bindDn       = args[2];
085        final char[] bindPassword = args[3].toCharArray();
086
087        // Read an entry from System.in.
088        final Entry entry;
089        try {
090            System.out.println("Enter entry to add in LDIF format:");
091            entry = new LDIFEntryReader(System.in).readEntry();
092        } catch (IOException e) {
093            System.err.println(e.getMessage());
094            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
095            return;
096        }
097        final String entryDn = entry.getName().toString();
098
099        // Connect, bind, read schema, and add entry if valid according to schema.
100        new LDAPConnectionFactory(host, port)
101                .getConnectionAsync()
102                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
103                    @Override
104                    public Promise<BindResult, LdapException> apply(Connection connection)
105                            throws LdapException {
106                        UseSchemaAsync.connection = connection;
107                        return connection.bindAsync(
108                                Requests.newSimpleBindRequest(bindDn, bindPassword));
109                    }
110                })
111                .thenAsync(new AsyncFunction<BindResult, Schema, LdapException>() {
112                    @Override
113                    public Promise<Schema, LdapException> apply(BindResult bindResult)
114                            throws LdapException {
115                        return Schema.readSchemaForEntryAsync(connection, DN.rootDN());
116                    }
117                })
118                .thenAsync(new AsyncFunction<Schema, Result, LdapException>() {
119                    @Override
120                    public Promise<Result, LdapException> apply(Schema schema)
121                            throws LdapException {
122                        final List<LocalizableMessage> schemaErrors = new LinkedList<>();
123                        boolean isValid = schema.validateEntry(
124                                entry,
125                                SchemaValidationPolicy.defaultPolicy(),
126                                schemaErrors);
127                        if (isValid) {
128                            System.out.println("Processing ADD request for " + entryDn);
129                            return connection.addAsync(Requests.newAddRequest(entry));
130                        } else {
131                            for (LocalizableMessage error : schemaErrors) {
132                                System.err.println(error);
133                            }
134                            return Promises.newExceptionPromise(
135                                    LdapException.newLdapException(
136                                            ResultCode.CLIENT_SIDE_PARAM_ERROR,
137                                            "Entry does not conform to schema."));
138                        }
139                    }
140                })
141                .thenOnResult(new ResultHandler<Result>() {
142                    @Override
143                    public void handleResult(Result result) {
144                        System.out.println("ADD operation successful for DN " + entryDn);
145                        resultCode = result.getResultCode().intValue();
146                        COMPLETION_LATCH.countDown();
147                    }
148                })
149                .thenOnException(new ExceptionHandler<LdapException>() {
150                    @Override
151                    public void handleException(LdapException e) {
152                        System.err.println(e.getMessage());
153                        resultCode = e.getResult().getResultCode().intValue();
154                        COMPLETION_LATCH.countDown();
155                    }
156                });
157
158        try {
159            COMPLETION_LATCH.await();
160        }  catch (InterruptedException e) {
161            System.err.println(e.getMessage());
162            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
163            return;
164        }
165
166        closeSilently(connection);
167        System.exit(resultCode);
168    }
169
170    private UseSchemaAsync() {
171        // Not used.
172    }
173}