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.i18n.LocalizableMessage; 021import org.forgerock.opendj.ldap.Connection; 022import org.forgerock.opendj.ldap.DN; 023import org.forgerock.opendj.ldap.Entry; 024import org.forgerock.opendj.ldap.LDAPConnectionFactory; 025import org.forgerock.opendj.ldap.LdapException; 026import org.forgerock.opendj.ldap.ResultCode; 027import org.forgerock.opendj.ldap.requests.Requests; 028import org.forgerock.opendj.ldap.responses.BindResult; 029import org.forgerock.opendj.ldap.responses.Result; 030import org.forgerock.opendj.ldap.schema.Schema; 031import org.forgerock.opendj.ldap.schema.SchemaValidationPolicy; 032import org.forgerock.opendj.ldif.LDIFEntryReader; 033import org.forgerock.util.AsyncFunction; 034import org.forgerock.util.promise.ExceptionHandler; 035import org.forgerock.util.promise.Promise; 036import org.forgerock.util.promise.Promises; 037import org.forgerock.util.promise.ResultHandler; 038 039import java.io.IOException; 040import java.util.LinkedList; 041import java.util.List; 042import java.util.concurrent.CountDownLatch; 043 044/** 045 * This example command-line client application validates an entry 046 * against the directory server schema before adding it 047 * using the asynchronous APIs. 048 * 049 * <br> 050 * 051 * This example takes the following command line parameters: 052 * 053 * <pre> 054 * {@code <host> <port> <bindDN> <bindPassword>} 055 * </pre> 056 * 057 * Then it reads an entry to add from System.in. 058 * If the entry is valid according to the directory schema, 059 * it tries to add the entry to the directory. 060 */ 061public final class UseSchemaAsync { 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 * Main method. 071 * 072 * @param args 073 * The command line arguments: host, port, bindDN, bindPassword. 074 */ 075 public static void main(final String[] args) { 076 if (args.length != 4) { 077 System.err.println("Usage: host port bindDN bindPassword"); 078 System.exit(1); 079 } 080 081 // Parse command line arguments. 082 final String host = args[0]; 083 final int port = Integer.parseInt(args[1]); 084 final String bindDn = args[2]; 085 final char[] bindPassword = args[3].toCharArray(); 086 087 // Read an entry from System.in. 088 final Entry entry; 089 try { 090 System.out.println("Enter entry to add in LDIF format:"); 091 entry = new LDIFEntryReader(System.in).readEntry(); 092 } catch (IOException e) { 093 System.err.println(e.getMessage()); 094 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 095 return; 096 } 097 final String entryDn = entry.getName().toString(); 098 099 // Connect, bind, read schema, and add entry if valid according to schema. 100 new LDAPConnectionFactory(host, port) 101 .getConnectionAsync() 102 .thenAsync(new AsyncFunction<Connection, BindResult, LdapException>() { 103 @Override 104 public Promise<BindResult, LdapException> apply(Connection connection) 105 throws LdapException { 106 UseSchemaAsync.connection = connection; 107 return connection.bindAsync( 108 Requests.newSimpleBindRequest(bindDn, bindPassword)); 109 } 110 }) 111 .thenAsync(new AsyncFunction<BindResult, Schema, LdapException>() { 112 @Override 113 public Promise<Schema, LdapException> apply(BindResult bindResult) 114 throws LdapException { 115 return Schema.readSchemaForEntryAsync(connection, DN.rootDN()); 116 } 117 }) 118 .thenAsync(new AsyncFunction<Schema, Result, LdapException>() { 119 @Override 120 public Promise<Result, LdapException> apply(Schema schema) 121 throws LdapException { 122 final List<LocalizableMessage> schemaErrors = new LinkedList<>(); 123 boolean isValid = schema.validateEntry( 124 entry, 125 SchemaValidationPolicy.defaultPolicy(), 126 schemaErrors); 127 if (isValid) { 128 System.out.println("Processing ADD request for " + entryDn); 129 return connection.addAsync(Requests.newAddRequest(entry)); 130 } else { 131 for (LocalizableMessage error : schemaErrors) { 132 System.err.println(error); 133 } 134 return Promises.newExceptionPromise( 135 LdapException.newLdapException( 136 ResultCode.CLIENT_SIDE_PARAM_ERROR, 137 "Entry does not conform to schema.")); 138 } 139 } 140 }) 141 .thenOnResult(new ResultHandler<Result>() { 142 @Override 143 public void handleResult(Result result) { 144 System.out.println("ADD operation successful for DN " + entryDn); 145 resultCode = result.getResultCode().intValue(); 146 COMPLETION_LATCH.countDown(); 147 } 148 }) 149 .thenOnException(new ExceptionHandler<LdapException>() { 150 @Override 151 public void handleException(LdapException e) { 152 System.err.println(e.getMessage()); 153 resultCode = e.getResult().getResultCode().intValue(); 154 COMPLETION_LATCH.countDown(); 155 } 156 }); 157 158 try { 159 COMPLETION_LATCH.await(); 160 } catch (InterruptedException e) { 161 System.err.println(e.getMessage()); 162 System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue()); 163 return; 164 } 165 166 closeSilently(connection); 167 System.exit(resultCode); 168 } 169 170 private UseSchemaAsync() { 171 // Not used. 172 } 173}