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 * Copyright 2015 ForgeRock AS. 024 * 025 */ 026 027package org.forgerock.opendj.examples; 028 029import static org.forgerock.util.Utils.closeSilently; 030import org.forgerock.opendj.ldap.Connection; 031import org.forgerock.opendj.ldap.Entries; 032import org.forgerock.opendj.ldap.Entry; 033import org.forgerock.opendj.ldap.LDAPConnectionFactory; 034import org.forgerock.opendj.ldap.LdapException; 035import org.forgerock.opendj.ldap.LinkedHashMapEntry; 036import org.forgerock.opendj.ldap.ResultCode; 037import org.forgerock.opendj.ldap.TreeMapEntry; 038import org.forgerock.opendj.ldap.requests.ModifyRequest; 039import org.forgerock.opendj.ldap.requests.Requests; 040import org.forgerock.opendj.ldap.responses.BindResult; 041import org.forgerock.opendj.ldap.responses.Result; 042import org.forgerock.opendj.ldif.LDIFEntryWriter; 043import org.forgerock.util.AsyncFunction; 044import org.forgerock.util.promise.ExceptionHandler; 045import org.forgerock.util.promise.Promise; 046import org.forgerock.util.promise.ResultHandler; 047 048import java.io.IOException; 049import java.util.concurrent.CountDownLatch; 050 051/** 052 * A command-line client that creates, updates, renames, and deletes a 053 * short-lived entry in order to demonstrate LDAP write operations 054 * using the asynchronous APIs. 055 * <br> 056 * The client takes as arguments the host and port for the directory server, 057 * and expects to find the entries and access control instructions as defined in 058 * <a href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>. 059 * 060 * <ul> 061 * <li>host - host name of the directory server</li> 062 * <li>port - port number of the directory server</li> 063 * </ul> 064 * 065 * All arguments are required. 066 */ 067public final class ShortLifeAsync { 068 /** The short-lived entry. */ 069 private static Entry entry; 070 /** Writer for displaying LDIF to System.out. */ 071 private static LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 072 /** Connection to the LDAP server. */ 073 private static Connection connection; 074 /** Result for the operation. */ 075 private static int resultCode; 076 /** Count down latch to wait for modify operation to complete. */ 077 private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1); 078 079 /** 080 * Adds, modifies, renames, and deletes an entry. 081 * 082 * @param args 083 * The command line arguments: host, port 084 */ 085 public static void main(final String[] args) { 086 if (args.length != 2) { 087 System.err.println("Usage: host port"); 088 System.err.println("For example: localhost 1389"); 089 System.exit(1); 090 } 091 final String host = args[0]; 092 final int port = Integer.parseInt(args[1]); 093 094 // User credentials of a "Directory Administrators" group member. 095 // Kirsten Vaughan is authorized to create, update, and delete entries. 096 // 097 // Alternatively, prompt an administrator user for credentials, 098 // or get the application its own account with access to update data. 099 final String adminDn = "uid=kvaughan,ou=people,dc=example,dc=com"; 100 final char[] adminPwd = "bribery".toCharArray(); 101 102 // Prepare an entry to add to the directory. 103 final String entryDn = "cn=Bob,ou=People,dc=example,dc=com"; 104 entry = new LinkedHashMapEntry(entryDn) 105 .addAttribute("cn", "Bob") 106 .addAttribute("objectclass", "top") 107 .addAttribute("objectclass", "person") 108 .addAttribute("objectclass", "organizationalPerson") 109 .addAttribute("objectclass", "inetOrgPerson") 110 .addAttribute("mail", "subgenius@example.com") 111 .addAttribute("sn", "Dobbs"); 112 113 new LDAPConnectionFactory(host, port) 114 .getConnectionAsync() 115 .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() { 116 @Override 117 public Promise<BindResult, LdapException> apply(Connection connection) 118 throws LdapException { 119 ShortLifeAsync.connection = connection; 120 return connection.bindAsync( 121 Requests.newSimpleBindRequest(adminDn, adminPwd)); 122 } 123 }) 124 .thenAsync(new AsyncFunction<BindResult, Result, LdapException>() { 125 @Override 126 public Promise<Result, LdapException> apply(BindResult bindResult) 127 throws LdapException { 128 log("Adding the entry..."); 129 log(entry); 130 return connection.addAsync(Requests.newAddRequest(entry)); 131 } 132 }) 133 .thenAsync(new AsyncFunction<Result, Result, LdapException>() { 134 @Override 135 public Promise<Result, LdapException> apply(Result result) 136 throws LdapException { 137 Entry old = TreeMapEntry.deepCopyOfEntry(entry); 138 entry = entry 139 .replaceAttribute("mail", "spammer@example.com") 140 .addAttribute("description", "Good user gone bad"); 141 log("Updating mail address, adding description..."); 142 log(entry); 143 ModifyRequest request = Entries.diffEntries(old, entry); 144 return connection.modifyAsync(request); 145 } 146 }) 147 .thenAsync(new AsyncFunction<Result, Result, LdapException>() { 148 @Override 149 public Promise<Result, LdapException> apply(Result result) 150 throws LdapException { 151 entry = entry.setName("cn=Renamed,ou=People,dc=example,dc=com"); 152 log("Renaming the entry..."); 153 log(entry); 154 return connection.modifyDNAsync( 155 Requests.newModifyDNRequest(entryDn, "cn=Renamed")); 156 } 157 }) 158 .thenAsync(new AsyncFunction<Result, Result, LdapException>() { 159 @Override 160 public Promise<Result, LdapException> apply(Result result) 161 throws LdapException { 162 final String newDn = entryDn.replace("Bob", "Renamed"); 163 log("Deleting " + newDn + "..."); 164 return connection.deleteAsync( 165 Requests.newDeleteRequest(newDn)); 166 } 167 }) 168 .thenOnResult(new ResultHandler<Result>() { 169 @Override 170 public void handleResult(Result result) { 171 resultCode = result.getResultCode().intValue(); 172 log("... done."); 173 COMPLETION_LATCH.countDown(); 174 } 175 }) 176 .thenOnException(new ExceptionHandler<LdapException>() { 177 @Override 178 public void handleException(LdapException e) { 179 System.err.println(e.getMessage()); 180 resultCode = e.getResult().getResultCode().intValue(); 181 COMPLETION_LATCH.countDown(); 182 } 183 }); 184 185 try { 186 COMPLETION_LATCH.await(); 187 } catch (InterruptedException e) { 188 System.err.println(e.getMessage()); 189 System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue()); 190 return; 191 } 192 193 closeSilently(connection); 194 System.exit(resultCode); 195 } 196 197 /** 198 * Log a message to System.out. 199 * 200 * @param message The message to write to the console. 201 */ 202 private static void log(final String message) { 203 System.out.println(message); 204 } 205 206 /** 207 * Log an entry in LDIF form. 208 * 209 * @param entry The entry to log. 210 */ 211 private static void log(final Entry entry) { 212 try { 213 writer.writeEntry(entry); 214 writer.flush(); 215 } catch (IOException e) { 216 System.err.println(e.getMessage()); 217 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 218 } 219 } 220 221 /** 222 * Constructor not used. 223 */ 224 private ShortLifeAsync() { 225 // Not used. 226 } 227}