View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions Copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2015 ForgeRock AS.
15   */
16  
17  package org.forgerock.opendj.examples;
18  
19  import static org.forgerock.util.Utils.closeSilently;
20  import org.forgerock.opendj.ldap.Connection;
21  import org.forgerock.opendj.ldap.Entries;
22  import org.forgerock.opendj.ldap.Entry;
23  import org.forgerock.opendj.ldap.LDAPConnectionFactory;
24  import org.forgerock.opendj.ldap.LdapException;
25  import org.forgerock.opendj.ldap.LinkedHashMapEntry;
26  import org.forgerock.opendj.ldap.ResultCode;
27  import org.forgerock.opendj.ldap.TreeMapEntry;
28  import org.forgerock.opendj.ldap.requests.ModifyRequest;
29  import org.forgerock.opendj.ldap.requests.Requests;
30  import org.forgerock.opendj.ldap.responses.BindResult;
31  import org.forgerock.opendj.ldap.responses.Result;
32  import org.forgerock.opendj.ldif.LDIFEntryWriter;
33  import org.forgerock.util.AsyncFunction;
34  import org.forgerock.util.promise.ExceptionHandler;
35  import org.forgerock.util.promise.Promise;
36  import org.forgerock.util.promise.ResultHandler;
37  
38  import java.io.IOException;
39  import java.util.concurrent.CountDownLatch;
40  
41  /**
42   * A command-line client that creates, updates, renames, and deletes a
43   * short-lived entry in order to demonstrate LDAP write operations
44   * using the asynchronous APIs.
45   * <br>
46   * The client takes as arguments the host and port for the directory server,
47   * and expects to find the entries and access control instructions as defined in
48   * <a href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>.
49   *
50   * <ul>
51   * <li>host - host name of the directory server</li>
52   * <li>port - port number of the directory server</li>
53   * </ul>
54   *
55   * All arguments are required.
56   */
57  public final class ShortLifeAsync {
58      /** The short-lived entry. */
59      private static Entry entry;
60      /** Writer for displaying LDIF to System.out. */
61      private static LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
62      /** Connection to the LDAP server. */
63      private static Connection connection;
64      /** Result for the operation. */
65      private static int resultCode;
66      /** Count down latch to wait for modify operation to complete. */
67      private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
68  
69      /**
70       * Adds, modifies, renames, and deletes an entry.
71       *
72       * @param args
73       *            The command line arguments: host, port
74       */
75      public static void main(final String[] args) {
76          if (args.length != 2) {
77              System.err.println("Usage: host port");
78              System.err.println("For example: localhost 1389");
79              System.exit(1);
80          }
81          final String host = args[0];
82          final int    port = Integer.parseInt(args[1]);
83  
84          // User credentials of a "Directory Administrators" group member.
85          // Kirsten Vaughan is authorized to create, update, and delete entries.
86          //
87          // Alternatively, prompt an administrator user for credentials,
88          // or get the application its own account with access to update data.
89          final String adminDn  = "uid=kvaughan,ou=people,dc=example,dc=com";
90          final char[] adminPwd = "bribery".toCharArray();
91  
92          // Prepare an entry to add to the directory.
93          final String entryDn = "cn=Bob,ou=People,dc=example,dc=com";
94          entry = new LinkedHashMapEntry(entryDn)
95              .addAttribute("cn", "Bob")
96              .addAttribute("objectclass", "top")
97              .addAttribute("objectclass", "person")
98              .addAttribute("objectclass", "organizationalPerson")
99              .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 }