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