001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2015 ForgeRock AS.
025 */
026package org.forgerock.opendj.examples;
027
028import java.io.IOException;
029import java.util.Arrays;
030import java.util.concurrent.CountDownLatch;
031
032import org.forgerock.opendj.ldap.Connection;
033import org.forgerock.opendj.ldap.LDAPConnectionFactory;
034import org.forgerock.opendj.ldap.LdapException;
035import org.forgerock.opendj.ldap.LdapPromise;
036import org.forgerock.opendj.ldap.ResultCode;
037import org.forgerock.opendj.ldap.SearchResultHandler;
038import org.forgerock.opendj.ldap.SearchScope;
039import org.forgerock.opendj.ldap.requests.CancelExtendedRequest;
040import org.forgerock.opendj.ldap.requests.Requests;
041import org.forgerock.opendj.ldap.responses.BindResult;
042import org.forgerock.opendj.ldap.responses.ExtendedResult;
043import org.forgerock.opendj.ldap.responses.Result;
044import org.forgerock.opendj.ldap.responses.SearchResultEntry;
045import org.forgerock.opendj.ldap.responses.SearchResultReference;
046import org.forgerock.opendj.ldif.LDIFEntryWriter;
047import org.forgerock.util.AsyncFunction;
048import org.forgerock.util.promise.ExceptionHandler;
049import org.forgerock.util.promise.Promise;
050import org.forgerock.util.promise.ResultHandler;
051
052/**
053 * An example client application which searches a Directory Server using the
054 * asynchronous APIs. This example takes the following command line parameters:
055 *
056 * <pre>
057 *  {@code <host> <port> <username> <password>
058 *      <baseDN> <scope> <filter> [<attribute> <attribute> ...]}
059 * </pre>
060 */
061public final class SearchAsync {
062    // --- JCite search result handler ---
063    private static final class SearchResultHandlerImpl implements SearchResultHandler {
064        /** {@inheritDoc} */
065        @Override
066        public synchronized boolean handleEntry(final SearchResultEntry entry) {
067            try {
068                if (entryCount < 10) {
069                    WRITER.writeComment("Search result entry: " + entry.getName());
070                    WRITER.writeEntry(entry);
071                    ++entryCount;
072                } else { // Cancel the search.
073                    CancelExtendedRequest request =
074                            Requests.newCancelExtendedRequest(requestID);
075                    connection.extendedRequestAsync(request)
076                            .thenOnResult(new ResultHandler<ExtendedResult>() {
077                                @Override
078                                public void handleResult(ExtendedResult result) {
079                                    System.err.println("Cancel request succeeded");
080                                    CANCEL_LATCH.countDown();
081                                }
082                            })
083                            .thenOnException(new ExceptionHandler<LdapException>() {
084                                @Override
085                                public void handleException(LdapException exception) {
086                                    System.err.println("Cancel request failed: "
087                                            + exception.getResult().getResultCode().intValue()
088                                            + " "
089                                            + exception.getResult().getDiagnosticMessage());
090                                    CANCEL_LATCH.countDown();
091                                }
092                            });
093                    return false;
094                }
095            } catch (final IOException e) {
096                System.err.println(e.getMessage());
097                resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
098                COMPLETION_LATCH.countDown();
099                return false;
100            }
101            return true;
102        }
103
104        /** {@inheritDoc} */
105        @Override
106        public synchronized boolean handleReference(final SearchResultReference reference) {
107            try {
108                WRITER.writeComment("Search result reference: " + reference.getURIs());
109            } catch (final IOException e) {
110                System.err.println(e.getMessage());
111                resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
112                COMPLETION_LATCH.countDown();
113                return false;
114            }
115            return true;
116        }
117
118    }
119    // --- JCite search result handler ---
120
121    // --- JCite decl1 ---
122    private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
123    private static final CountDownLatch CANCEL_LATCH = new CountDownLatch(1);
124    private static final LDIFEntryWriter WRITER = new LDIFEntryWriter(System.out);
125    // --- JCite decl1 ---
126    private static String userName;
127    private static String password;
128    private static String baseDN;
129    private static SearchScope scope;
130    private static String filter;
131    private static String[] attributes;
132    private static Connection connection;
133    private static int resultCode;
134
135    // --- JCite decl2 ---
136    static int requestID;
137    static int entryCount;
138    // --- JCite decl2 ---
139
140    /**
141     * Main method.
142     *
143     * @param args
144     *            The command line arguments: host, port, username, password,
145     *            base DN, scope, filter, and zero or more attributes to be
146     *            retrieved.
147     */
148    public static void main(final String[] args) {
149        if (args.length < 7) {
150            System.err.println("Usage: host port username password baseDN scope " + "filter [attribute ...]");
151            System.exit(1);
152        }
153
154        // Parse command line arguments.
155        final String hostName = args[0];
156        final int port = Integer.parseInt(args[1]);
157        userName = args[2];
158        password = args[3];
159        baseDN = args[4];
160        final String scopeString = args[5];
161        filter = args[6];
162        if (args.length > 7) {
163            attributes = Arrays.copyOfRange(args, 7, args.length);
164        } else {
165            attributes = new String[0];
166        }
167
168        scope = SearchScope.valueOf(scopeString);
169        if (scope == null) {
170            System.err.println("Unknown scope: " + scopeString);
171            System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
172            return;
173        }
174
175        // --- Using Promises ---
176        // Initiate the asynchronous connect, bind, and search.
177        final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName, port);
178
179        factory.getConnectionAsync()
180                .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() {
181                    @Override
182                    public Promise<BindResult, LdapException> apply(Connection connection)
183                            throws LdapException {
184                        SearchAsync.connection = connection;
185                        return connection.bindAsync(Requests
186                                .newSimpleBindRequest(userName, password.toCharArray()));
187                    }
188                })
189                .thenAsync(new AsyncFunction<BindResult, Result, LdapException>() {
190                    @Override
191                    public Promise<Result, LdapException> apply(BindResult result)
192                            throws LdapException {
193                        LdapPromise<Result> promise = connection.searchAsync(
194                                Requests.newSearchRequest(baseDN, scope, filter, attributes),
195                                new SearchResultHandlerImpl());
196                        requestID = promise.getRequestID();
197                        return promise;
198                    }
199                })
200                .thenOnResult(new ResultHandler<Result>() {
201                    @Override
202                    public void handleResult(Result result) {
203                        resultCode = result.getResultCode().intValue();
204                        COMPLETION_LATCH.countDown();
205                    }
206                })
207                .thenOnException(new ExceptionHandler<LdapException>() {
208                    @Override
209                    public void handleException(LdapException exception) {
210                        System.err.println(exception.getMessage());
211                        resultCode = exception.getResult().getResultCode().intValue();
212                        COMPLETION_LATCH.countDown();
213                    }
214                });
215        // --- Using Promises ---
216
217        // Await completion.
218        try {
219            COMPLETION_LATCH.await();
220        } catch (final InterruptedException e) {
221            System.err.println(e.getMessage());
222            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
223            return;
224        }
225
226        try {
227            WRITER.flush();
228        } catch (final IOException e) {
229            System.err.println(e.getMessage());
230            System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
231            return;
232        }
233
234        // Await completion of the cancel request.
235        try {
236            CANCEL_LATCH.await();
237        } catch (final InterruptedException e) {
238            System.err.println(e.getMessage());
239            System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
240            return;
241        }
242
243        if (connection != null) {
244            connection.close();
245        }
246
247        System.exit(resultCode);
248    }
249
250    private SearchAsync() {
251        // Not used.
252    }
253}