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