001/*
002 * Copyright 2011-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2011-2014 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.listener;
022
023
024
025import java.net.InetAddress;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.LinkedHashMap;
031import java.util.List;
032import java.util.Map;
033import javax.net.SocketFactory;
034
035import com.unboundid.asn1.ASN1OctetString;
036import com.unboundid.ldap.listener.interceptor.
037            InMemoryOperationInterceptorRequestHandler;
038import com.unboundid.ldap.protocol.AddRequestProtocolOp;
039import com.unboundid.ldap.protocol.AddResponseProtocolOp;
040import com.unboundid.ldap.protocol.BindRequestProtocolOp;
041import com.unboundid.ldap.protocol.BindResponseProtocolOp;
042import com.unboundid.ldap.protocol.CompareRequestProtocolOp;
043import com.unboundid.ldap.protocol.CompareResponseProtocolOp;
044import com.unboundid.ldap.protocol.DeleteRequestProtocolOp;
045import com.unboundid.ldap.protocol.DeleteResponseProtocolOp;
046import com.unboundid.ldap.protocol.ExtendedRequestProtocolOp;
047import com.unboundid.ldap.protocol.ExtendedResponseProtocolOp;
048import com.unboundid.ldap.protocol.LDAPMessage;
049import com.unboundid.ldap.protocol.ModifyRequestProtocolOp;
050import com.unboundid.ldap.protocol.ModifyResponseProtocolOp;
051import com.unboundid.ldap.protocol.ModifyDNRequestProtocolOp;
052import com.unboundid.ldap.protocol.ModifyDNResponseProtocolOp;
053import com.unboundid.ldap.protocol.SearchRequestProtocolOp;
054import com.unboundid.ldap.protocol.SearchResultDoneProtocolOp;
055import com.unboundid.ldap.sdk.AddRequest;
056import com.unboundid.ldap.sdk.Attribute;
057import com.unboundid.ldap.sdk.BindRequest;
058import com.unboundid.ldap.sdk.BindResult;
059import com.unboundid.ldap.sdk.CompareRequest;
060import com.unboundid.ldap.sdk.CompareResult;
061import com.unboundid.ldap.sdk.Control;
062import com.unboundid.ldap.sdk.DeleteRequest;
063import com.unboundid.ldap.sdk.DereferencePolicy;
064import com.unboundid.ldap.sdk.DN;
065import com.unboundid.ldap.sdk.Entry;
066import com.unboundid.ldap.sdk.ExtendedRequest;
067import com.unboundid.ldap.sdk.ExtendedResult;
068import com.unboundid.ldap.sdk.Filter;
069import com.unboundid.ldap.sdk.InternalSDKHelper;
070import com.unboundid.ldap.sdk.LDAPConnection;
071import com.unboundid.ldap.sdk.LDAPConnectionOptions;
072import com.unboundid.ldap.sdk.LDAPConnectionPool;
073import com.unboundid.ldap.sdk.LDAPException;
074import com.unboundid.ldap.sdk.LDAPInterface;
075import com.unboundid.ldap.sdk.LDAPResult;
076import com.unboundid.ldap.sdk.LDAPSearchException;
077import com.unboundid.ldap.sdk.Modification;
078import com.unboundid.ldap.sdk.ModifyRequest;
079import com.unboundid.ldap.sdk.ModifyDNRequest;
080import com.unboundid.ldap.sdk.PLAINBindRequest;
081import com.unboundid.ldap.sdk.ReadOnlyAddRequest;
082import com.unboundid.ldap.sdk.ReadOnlyCompareRequest;
083import com.unboundid.ldap.sdk.ReadOnlyDeleteRequest;
084import com.unboundid.ldap.sdk.ReadOnlyModifyRequest;
085import com.unboundid.ldap.sdk.ReadOnlyModifyDNRequest;
086import com.unboundid.ldap.sdk.ReadOnlySearchRequest;
087import com.unboundid.ldap.sdk.ResultCode;
088import com.unboundid.ldap.sdk.RootDSE;
089import com.unboundid.ldap.sdk.SearchRequest;
090import com.unboundid.ldap.sdk.SearchResult;
091import com.unboundid.ldap.sdk.SearchResultEntry;
092import com.unboundid.ldap.sdk.SearchResultListener;
093import com.unboundid.ldap.sdk.SearchResultReference;
094import com.unboundid.ldap.sdk.SearchScope;
095import com.unboundid.ldap.sdk.SimpleBindRequest;
096import com.unboundid.ldap.sdk.schema.Schema;
097import com.unboundid.ldif.LDIFException;
098import com.unboundid.ldif.LDIFReader;
099import com.unboundid.ldif.LDIFWriter;
100import com.unboundid.util.ByteStringBuffer;
101import com.unboundid.util.Debug;
102import com.unboundid.util.Mutable;
103import com.unboundid.util.StaticUtils;
104import com.unboundid.util.ThreadSafety;
105import com.unboundid.util.ThreadSafetyLevel;
106import com.unboundid.util.Validator;
107
108import static com.unboundid.ldap.listener.ListenerMessages.*;
109
110
111
112/**
113 * This class provides a utility that may be used to create a simple LDAP server
114 * instance that will hold all of its information in memory.  It is intended to
115 * be very easy to use, particularly as an embeddable server for testing
116 * directory-enabled applications.  It can be easily created, configured,
117 * populated, and shut down with only a few lines of code, and it provides a
118 * number of convenience methods that can be very helpful in writing test cases
119 * that validate the content of the server.
120 * <BR><BR>
121 * Some notes about the capabilities of this server:
122 * <UL>
123 *   <LI>It provides reasonably complete support for add, compare, delete,
124 *       modify, modify DN (including new superior and subtree move/rename),
125 *       search, and unbind operations.</LI>
126 *   <LI>It will accept abandon requests, but will not do anything with
127 *       them.</LI>
128 *   <LI>It provides support for simple bind operations, and for the SASL PLAIN
129 *       mechanism.  It also provides an API that can be used to add support for
130 *       additional SASL mechanisms.</LI>
131 *   <LI>It provides support for the password modify, StartTLS, and "who am I?"
132 *       extended operations, as well as an API that can be used to add support
133 *       for additional types of extended operations.</LI>
134 *   <LI>It provides support for the LDAP assertions, authorization identity,
135 *       don't use copy, manage DSA IT, permissive modify, pre-read, post-read,
136 *       proxied authorization v1 and v2, server-side sort, simple paged
137 *       results, LDAP subentries, subtree delete, and virtual list view request
138 *       controls.</LI>
139 *   <LI>It supports the use of schema (if provided), but it does not currently
140 *       allow updating the schema on the fly.</LI>
141 *   <LI>It has the ability to maintain a log of operations processed, either
142 *       as a simple access log or a more detailed LDAP debug log.</LI>
143 *   <LI>It has the ability to maintain an LDAP-accessible changelog.</LI>
144 *   <LI>It provides an option to generate a number of operational attributes,
145 *       including entryDN, entryUUID, creatorsName, createTimestamp,
146 *       modifiersName, modifyTimestamp, and subschemaSubentry.</LI>
147 *   <LI>It provides support for referential integrity, in which case specified
148 *       attributes whose values are DNs may be updated if the entries they
149 *       reference are deleted or renamed.</LI>
150 *   <LI>It provides methods for importing data from and exporting data to LDIF
151 *       files, and it has the ability to capture a point-in-time snapshot of
152 *       the data (including changelog information) that may be restored at any
153 *       point.</LI>
154 *   <LI>It implements the {@link LDAPInterface} interface, which means that in
155 *       many cases it can be used as a drop-in replacement for an
156 *       {@link LDAPConnection}.</LI>
157 * </UL>
158 * <BR><BR>
159 * In order to create an in-memory directory server instance, you should first
160 * create an {@link InMemoryDirectoryServerConfig} object with the desired
161 * settings.  Then use that configuration object to initialize the directory
162 * server instance, and call the {@link #startListening} method to start
163 * accepting connections from LDAP clients.  The {@link #getConnection} and
164 * {@link #getConnectionPool} methods may be used to obtain connections to the
165 * server and you can also manually create connections using the information
166 * obtained via the {@link #getListenAddress}, {@link #getListenPort}, and
167 * {@link #getClientSocketFactory} methods.  When the server is no longer
168 * needed, the {@link #shutDown} method should be used to stop the server.  Any
169 * number of in-memory directory server instances can be created and running in
170 * a single JVM at any time, and many of the methods provided in this class can
171 * be used without the server running if operations are to be performed using
172 * only method calls rather than via LDAP clients.
173 * <BR><BR>
174 * <H2>Example</H2>
175 * The following example demonstrates the process that can be used to create,
176 * start, and use an in-memory directory server instance, including support for
177 * secure communication using both SSL and StartTLS:
178 * <PRE>
179 * // Create a base configuration for the server.
180 * InMemoryDirectoryServerConfig config =
181 *      new InMemoryDirectoryServerConfig("dc=example,dc=com");
182 * config.addAdditionalBindCredentials("cn=Directory Manager",
183 *      "password");
184 *
185 * // Update the configuration to support LDAP (with StartTLS) and LDAPS
186 * // listeners.
187 * final SSLUtil serverSSLUtil = new SSLUtil(
188 *      new KeyStoreKeyManager(serverKeyStorePath, serverKeyStorePIN, "JKS",
189 *           "server-cert"),
190 *      new TrustStoreTrustManager(serverTrustStorePath));
191 * final SSLUtil clientSSLUtil = new SSLUtil(
192 *      new TrustStoreTrustManager(clientTrustStorePath));
193 * config.setListenerConfigs(
194 *      InMemoryListenerConfig.createLDAPConfig("LDAP", // Listener name
195 *           null, // Listen address. (null = listen on all interfaces)
196 *           0, // Listen port (0 = automatically choose an available port)
197 *           serverSSLUtil.createSSLSocketFactory()), // StartTLS factory
198 *      InMemoryListenerConfig.createLDAPSConfig("LDAPS", // Listener name
199 *           null, // Listen address. (null = listen on all interfaces)
200 *           0, // Listen port (0 = automatically choose an available port)
201 *           serverSSLUtil.createSSLServerSocketFactory(), // Server factory
202 *           clientSSLUtil.createSSLSocketFactory())); // Client factory
203 *
204 * // Create and start the server instance and populate it with an initial set
205 * // of data from an LDIF file.
206 * InMemoryDirectoryServer server = new InMemoryDirectoryServer(config);
207 * server.importFromLDIF(true, ldifFilePath);
208 *
209 * // Start the server so it will accept client connections.
210 * server.startListening();
211 *
212 * // Get an unencrypted connection to the server's LDAP listener, then use
213 * // StartTLS to secure that connection.  Make sure the connection is usable
214 * // by retrieving the server root DSE.
215 * LDAPConnection connection = server.getConnection("LDAP");
216 * connection.processExtendedOperation(new StartTLSExtendedRequest(
217 *      clientSSLUtil.createSSLContext()));
218 * LDAPTestUtils.assertEntryExists(connection, "");
219 * connection.close();
220 *
221 * // Establish an SSL-based connection to the LDAPS listener, and make sure
222 * // that connection is also usable.
223 * connection = server.getConnection("LDAPS");
224 * LDAPTestUtils.assertEntryExists(connection, "");
225 * connection.close();
226 *
227 * // Shut down the server so that it will no longer accept client
228 * // connections, and close all existing connections.
229 * server.shutDown(true);
230 * </PRE>
231 */
232@Mutable()
233@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
234public final class InMemoryDirectoryServer
235       implements LDAPInterface
236{
237  // The in-memory request handler that will be used for the server.
238  private final InMemoryRequestHandler inMemoryHandler;
239
240  // The set of listeners that have been configured for this server, mapped by
241  // listener name.
242  private final Map<String,LDAPListener> listeners;
243
244  // The set of configurations for all the LDAP listeners to be used.
245  private final Map<String,LDAPListenerConfig> ldapListenerConfigs;
246
247  // The set of client socket factories associated with each of the listeners.
248  private final Map<String,SocketFactory> clientSocketFactories;
249
250  // A read-only representation of the configuration used to create this
251  // in-memory directory server.
252  private final ReadOnlyInMemoryDirectoryServerConfig config;
253
254
255
256  /**
257   * Creates a very simple instance of an in-memory directory server with the
258   * specified set of base DNs.  It will not use a well-defined schema, and will
259   * pick a listen port at random.
260   *
261   * @param  baseDNs  The base DNs to use for the server.  It must not be
262   *                  {@code null} or empty.
263   *
264   * @throws  LDAPException  If a problem occurs while attempting to initialize
265   *                         the server.
266   */
267  public InMemoryDirectoryServer(final String... baseDNs)
268         throws LDAPException
269  {
270    this(new InMemoryDirectoryServerConfig(baseDNs));
271  }
272
273
274
275  /**
276   * Creates a new instance of an in-memory directory server with the provided
277   * configuration.
278   *
279   * @param  cfg  The configuration to use for the server.  It must not be
280   *              {@code null}.
281   *
282   * @throws  LDAPException  If a problem occurs while trying to initialize the
283   *                         directory server with the provided configuration.
284   */
285  public InMemoryDirectoryServer(final InMemoryDirectoryServerConfig cfg)
286         throws LDAPException
287  {
288    Validator.ensureNotNull(cfg);
289
290    config = new ReadOnlyInMemoryDirectoryServerConfig(cfg);
291    inMemoryHandler = new InMemoryRequestHandler(config);
292
293    LDAPListenerRequestHandler requestHandler = inMemoryHandler;
294
295    if (config.getAccessLogHandler() != null)
296    {
297      requestHandler = new AccessLogRequestHandler(config.getAccessLogHandler(),
298           requestHandler);
299    }
300
301    if (config.getLDAPDebugLogHandler() != null)
302    {
303      requestHandler = new LDAPDebuggerRequestHandler(
304           config.getLDAPDebugLogHandler(), requestHandler);
305    }
306
307    if (! config.getOperationInterceptors().isEmpty())
308    {
309      requestHandler = new InMemoryOperationInterceptorRequestHandler(
310           config.getOperationInterceptors(), requestHandler);
311    }
312
313
314    final List<InMemoryListenerConfig> listenerConfigs =
315         config.getListenerConfigs();
316
317    listeners = new LinkedHashMap<String,LDAPListener>(listenerConfigs.size());
318    ldapListenerConfigs =
319         new LinkedHashMap<String,LDAPListenerConfig>(listenerConfigs.size());
320    clientSocketFactories =
321         new LinkedHashMap<String,SocketFactory>(listenerConfigs.size());
322
323    for (final InMemoryListenerConfig c : listenerConfigs)
324    {
325      final String name = StaticUtils.toLowerCase(c.getListenerName());
326
327      final LDAPListenerRequestHandler listenerRequestHandler;
328      if (c.getStartTLSSocketFactory() == null)
329      {
330        listenerRequestHandler =  requestHandler;
331      }
332      else
333      {
334        listenerRequestHandler =
335             new StartTLSRequestHandler(c.getStartTLSSocketFactory(),
336                  requestHandler);
337      }
338
339      final LDAPListenerConfig listenerCfg = new LDAPListenerConfig(
340           c.getListenPort(), listenerRequestHandler);
341      listenerCfg.setExceptionHandler(config.getListenerExceptionHandler());
342      listenerCfg.setListenAddress(c.getListenAddress());
343      listenerCfg.setServerSocketFactory(c.getServerSocketFactory());
344
345      ldapListenerConfigs.put(name, listenerCfg);
346
347      if (c.getClientSocketFactory() != null)
348      {
349        clientSocketFactories.put(name, c.getClientSocketFactory());
350      }
351    }
352  }
353
354
355
356  /**
357   * Attempts to start listening for client connections on all configured
358   * listeners.  Any listeners that are already running will be unaffected.
359   *
360   * @throws  LDAPException  If a problem occurs while attempting to create any
361   *                         of the configured listeners.  Even if an exception
362   *                         is thrown, then as many listeners as possible will
363   *                         be started.
364   */
365  public synchronized void startListening()
366         throws LDAPException
367  {
368    final ArrayList<String> messages = new ArrayList<String>(listeners.size());
369
370    for (final Map.Entry<String,LDAPListenerConfig> cfgEntry :
371         ldapListenerConfigs.entrySet())
372    {
373      final String name = cfgEntry.getKey();
374
375      if (listeners.containsKey(name))
376      {
377        // This listener is already running.
378        continue;
379      }
380
381      final LDAPListenerConfig listenerConfig = cfgEntry.getValue();
382      final LDAPListener listener = new LDAPListener(listenerConfig);
383
384      try
385      {
386        listener.startListening();
387        listenerConfig.setListenPort(listener.getListenPort());
388        listeners.put(name, listener);
389      }
390      catch (final Exception e)
391      {
392        Debug.debugException(e);
393        messages.add(ERR_MEM_DS_START_FAILED.get(name,
394             StaticUtils.getExceptionMessage(e)));
395      }
396    }
397
398    if (! messages.isEmpty())
399    {
400      throw new LDAPException(ResultCode.LOCAL_ERROR,
401           StaticUtils.concatenateStrings(messages));
402    }
403  }
404
405
406
407  /**
408   * Attempts to start listening for client connections on the specified
409   * listener.  If the listener is already running, then it will be unaffected.
410   *
411   * @param  listenerName  The name of the listener to be started.  It must not
412   *                       be {@code null}.
413   *
414   * @throws  LDAPException  If a problem occurs while attempting to start the
415   *                         requested listener.
416   */
417  public synchronized void startListening(final String listenerName)
418         throws LDAPException
419  {
420    // If the listener is already running, then there's nothing to do.
421    final String name = StaticUtils .toLowerCase(listenerName);
422    if (listeners.containsKey(name))
423    {
424      return;
425    }
426
427    // Get the configuration to use for the listener.
428    final LDAPListenerConfig listenerConfig = ldapListenerConfigs.get(name);
429    if (listenerConfig == null)
430    {
431      throw new LDAPException(ResultCode.PARAM_ERROR,
432           ERR_MEM_DS_NO_SUCH_LISTENER.get(listenerName));
433    }
434
435
436    final LDAPListener listener = new LDAPListener(listenerConfig);
437
438    try
439    {
440      listener.startListening();
441      listenerConfig.setListenPort(listener.getListenPort());
442      listeners.put(name, listener);
443    }
444    catch (final Exception e)
445    {
446      Debug.debugException(e);
447      throw new LDAPException(ResultCode.LOCAL_ERROR,
448           ERR_MEM_DS_START_FAILED.get(name,
449                StaticUtils.getExceptionMessage(e)),
450           e);
451    }
452  }
453
454
455
456  /**
457   * Shuts down all configured listeners.  Any listeners that are already
458   * stopped will be unaffected.
459   *
460   * @param  closeExistingConnections  Indicates whether to close all existing
461   *                                   connections, or merely to stop accepting
462   *                                   new connections.
463   */
464  public synchronized void shutDown(final boolean closeExistingConnections)
465  {
466    for (final LDAPListener l : listeners.values())
467    {
468      try
469      {
470        l.shutDown(closeExistingConnections);
471      }
472      catch (final Exception e)
473      {
474        Debug.debugException(e);
475      }
476    }
477
478    listeners.clear();
479  }
480
481
482
483  /**
484   * Shuts down the specified listener.  If there is no such listener defined,
485   * or if the specified listener is not running, then no action will be taken.
486   *
487   * @param  listenerName              The name of the listener to be shut down.
488   *                                   It must not be {@code null}.
489   * @param  closeExistingConnections  Indicates whether to close all existing
490   *                                   connections, or merely to stop accepting
491   *                                   new connections.
492   */
493  public synchronized void shutDown(final String listenerName,
494                                    final boolean closeExistingConnections)
495  {
496    final String name = StaticUtils.toLowerCase(listenerName);
497    final LDAPListener listener = listeners.remove(name);
498    if (listener != null)
499    {
500      listener.shutDown(closeExistingConnections);
501    }
502  }
503
504
505
506  /**
507   * Attempts to restart all listeners defined in the server.  All running
508   * listeners will be stopped, and all configured listeners will be started.
509   *
510   * @throws  LDAPException  If a problem occurs while attempting to restart any
511   *                         of the listeners.  Even if an exception is thrown,
512   *                         as many listeners as possible will be started.
513   */
514  public synchronized void restartServer()
515         throws LDAPException
516  {
517    shutDown(true);
518
519    try
520    {
521      Thread.sleep(100L);
522    }
523    catch (final Exception e)
524    {
525      Debug.debugException(e);
526    }
527
528    startListening();
529  }
530
531
532
533  /**
534   * Attempts to restart the specified listener.  If it is running, it will be
535   * stopped.  It will then be started.
536   *
537   * @param  listenerName  The name of the listener to be restarted.  It must
538   *                       not be {@code null}.
539   *
540   * @throws  LDAPException  If a problem occurs while attempting to restart the
541   *                         specified listener.
542   */
543  public synchronized void restartListener(final String listenerName)
544         throws LDAPException
545  {
546    shutDown(listenerName, true);
547
548    try
549    {
550      Thread.sleep(100L);
551    }
552    catch (final Exception e)
553    {
554      Debug.debugException(e);
555    }
556
557    startListening(listenerName);
558  }
559
560
561
562  /**
563   * Retrieves a read-only representation of the configuration used to create
564   * this in-memory directory server instance.
565   *
566   * @return  A read-only representation of the configuration used to create
567   *          this in-memory directory server instance.
568   */
569  public ReadOnlyInMemoryDirectoryServerConfig getConfig()
570  {
571    return config;
572  }
573
574
575
576  /**
577   * Retrieves the in-memory request handler that is used to perform the real
578   * server processing.
579   *
580   * @return  The in-memory request handler that is used to perform the real
581   *          server processing.
582   */
583  InMemoryRequestHandler getInMemoryRequestHandler()
584  {
585    return inMemoryHandler;
586  }
587
588
589
590  /**
591   * Creates a point-in-time snapshot of the information contained in this
592   * in-memory directory server instance.  It may be restored using the
593   * {@link #restoreSnapshot} method.
594   * <BR><BR>
595   * This method may be used regardless of whether the server is listening for
596   * client connections.
597   *
598   * @return  The snapshot created based on the current content of this
599   *          in-memory directory server instance.
600   */
601  public InMemoryDirectoryServerSnapshot createSnapshot()
602  {
603    return inMemoryHandler.createSnapshot();
604  }
605
606
607
608  /**
609   * Restores the this in-memory directory server instance to match the content
610   * it held at the time the snapshot was created.
611   * <BR><BR>
612   * This method may be used regardless of whether the server is listening for
613   * client connections.
614   *
615   * @param  snapshot  The snapshot to be restored.  It must not be
616   *                   {@code null}.
617   */
618  public void restoreSnapshot(final InMemoryDirectoryServerSnapshot snapshot)
619  {
620    inMemoryHandler.restoreSnapshot(snapshot);
621  }
622
623
624
625  /**
626   * Retrieves the list of base DNs configured for use by the server.
627   *
628   * @return  The list of base DNs configured for use by the server.
629   */
630  public List<DN> getBaseDNs()
631  {
632    return inMemoryHandler.getBaseDNs();
633  }
634
635
636
637  /**
638   * Attempts to establish a client connection to the server.  If multiple
639   * listeners are configured, then it will attempt to establish a connection to
640   * the first configured listener that is running.
641   *
642   * @return  The client connection that has been established.
643   *
644   * @throws  LDAPException  If a problem is encountered while attempting to
645   *                         create the connection.
646   */
647  public LDAPConnection getConnection()
648         throws LDAPException
649  {
650    return getConnection(null, null);
651  }
652
653
654
655  /**
656   * Attempts to establish a client connection to the server.
657   *
658   * @param  options  The connection options to use when creating the
659   *                  connection.  It may be {@code null} if a default set of
660   *                  options should be used.
661   *
662   * @return  The client connection that has been established.
663   *
664   * @throws  LDAPException  If a problem is encountered while attempting to
665   *                         create the connection.
666   */
667  public LDAPConnection getConnection(final LDAPConnectionOptions options)
668         throws LDAPException
669  {
670    return getConnection(null, options);
671  }
672
673
674
675  /**
676   * Attempts to establish a client connection to the specified listener.
677   *
678   * @param  listenerName  The name of the listener to which to establish the
679   *                       connection.  It may be {@code null} if a connection
680   *                       should be established to the first available
681   *                       listener.
682   *
683   * @return  The client connection that has been established.
684   *
685   * @throws  LDAPException  If a problem is encountered while attempting to
686   *                         create the connection.
687   */
688  public LDAPConnection getConnection(final String listenerName)
689         throws LDAPException
690  {
691    return getConnection(listenerName, null);
692  }
693
694
695
696  /**
697   * Attempts to establish a client connection to the specified listener.
698   *
699   * @param  listenerName  The name of the listener to which to establish the
700   *                       connection.  It may be {@code null} if a connection
701   *                       should be established to the first available
702   *                       listener.
703   * @param  options       The set of LDAP connection options to use for the
704   *                       connection that is created.
705   *
706   * @return  The client connection that has been established.
707   *
708   * @throws  LDAPException  If a problem is encountered while attempting to
709   *                         create the connection.
710   */
711  public synchronized LDAPConnection getConnection(final String listenerName,
712                                          final LDAPConnectionOptions options)
713         throws LDAPException
714  {
715    final LDAPListenerConfig listenerConfig;
716    final SocketFactory clientSocketFactory;
717
718    if (listenerName == null)
719    {
720      final String name = getFirstListenerName();
721      if (name == null)
722      {
723        throw new LDAPException(ResultCode.CONNECT_ERROR,
724             ERR_MEM_DS_GET_CONNECTION_NO_LISTENERS.get());
725      }
726
727      listenerConfig      = ldapListenerConfigs.get(name);
728      clientSocketFactory = clientSocketFactories.get(name);
729    }
730    else
731    {
732      final String name = StaticUtils.toLowerCase(listenerName);
733      if (! listeners.containsKey(name))
734      {
735        throw new LDAPException(ResultCode.CONNECT_ERROR,
736             ERR_MEM_DS_GET_CONNECTION_LISTENER_NOT_RUNNING.get(listenerName));
737      }
738
739      listenerConfig      = ldapListenerConfigs.get(name);
740      clientSocketFactory = clientSocketFactories.get(name);
741    }
742
743    String hostAddress;
744    final InetAddress listenAddress = listenerConfig.getListenAddress();
745    if ((listenAddress == null) || (listenAddress.isAnyLocalAddress()))
746    {
747      try
748      {
749        hostAddress = InetAddress.getLocalHost().getHostAddress();
750      }
751      catch (final Exception e)
752      {
753        Debug.debugException(e);
754        hostAddress = "127.0.0.1";
755      }
756    }
757    else
758    {
759      hostAddress = listenAddress.getHostAddress();
760    }
761
762    return new LDAPConnection(clientSocketFactory, options, hostAddress,
763         listenerConfig.getListenPort());
764  }
765
766
767
768  /**
769   * Attempts to establish a connection pool to the server with the specified
770   * maximum number of connections.
771   *
772   * @param  maxConnections  The maximum number of connections to maintain in
773   *                         the connection pool.  It must be greater than or
774   *                         equal to one.
775   *
776   * @return  The connection pool that has been created.
777   *
778   * @throws  LDAPException  If a problem occurs while attempting to create the
779   *                         connection pool.
780   */
781  public LDAPConnectionPool getConnectionPool(final int maxConnections)
782         throws LDAPException
783  {
784    return getConnectionPool(null, null, 1, maxConnections);
785  }
786
787
788
789  /**
790   * Attempts to establish a connection pool to the server with the provided
791   * settings.
792   *
793   * @param  listenerName        The name of the listener to which the
794   *                             connections should be established.
795   * @param  options             The connection options to use when creating
796   *                             connections for use in the pool.  It may be
797   *                             {@code null} if a default set of options should
798   *                             be used.
799   * @param  initialConnections  The initial number of connections to establish
800   *                             in the connection pool.  It must be greater
801   *                             than or equal to one.
802   * @param  maxConnections      The maximum number of connections to maintain
803   *                             in the connection pool.  It must be greater
804   *                             than or equal to the initial number of
805   *                             connections.
806   *
807   * @return  The connection pool that has been created.
808   *
809   * @throws  LDAPException  If a problem occurs while attempting to create the
810   *                         connection pool.
811   */
812  public LDAPConnectionPool getConnectionPool(final String listenerName,
813                                 final LDAPConnectionOptions options,
814                                 final int initialConnections,
815                                 final int maxConnections)
816         throws LDAPException
817  {
818    final LDAPConnection conn = getConnection(listenerName, options);
819    return new LDAPConnectionPool(conn, initialConnections, maxConnections);
820  }
821
822
823
824  /**
825   * Retrieves the configured listen address for the first active listener, if
826   * defined.
827   *
828   * @return  The configured listen address for the first active listener, or
829   *          {@code null} if that listener does not have an
830   *          explicitly-configured listen address or there are no active
831   *          listeners.
832   */
833  public InetAddress getListenAddress()
834  {
835    return getListenAddress(null);
836  }
837
838
839
840  /**
841   * Retrieves the configured listen address for the specified listener, if
842   * defined.
843   *
844   * @param  listenerName  The name of the listener for which to retrieve the
845   *                       listen address.  It may be {@code null} in order to
846   *                       obtain the listen address for the first active
847   *                       listener.
848   *
849   * @return  The configured listen address for the specified listener, or
850   *          {@code null} if there is no such listener or the listener does not
851   *          have an explicitly-configured listen address.
852   */
853  public synchronized InetAddress getListenAddress(final String listenerName)
854  {
855    final String name;
856    if (listenerName == null)
857    {
858      name = getFirstListenerName();
859    }
860    else
861    {
862      name = StaticUtils.toLowerCase(listenerName);
863    }
864
865    final LDAPListenerConfig listenerCfg = ldapListenerConfigs.get(name);
866    if (listenerCfg == null)
867    {
868      return null;
869    }
870    else
871    {
872      return listenerCfg.getListenAddress();
873    }
874  }
875
876
877
878  /**
879   * Retrieves the configured listen port for the first active listener.
880   *
881   * @return  The configured listen port for the first active listener, or -1 if
882   *          there are no active listeners.
883   */
884  public int getListenPort()
885  {
886    return getListenPort(null);
887  }
888
889
890
891  /**
892   * Retrieves the configured listen port for the specified listener, if
893   * available.
894   *
895   * @param  listenerName  The name of the listener for which to retrieve the
896   *                       listen port.  It may be {@code null} in order to
897   *                       obtain the listen port for the first active
898   *                       listener.
899   *
900   * @return  The configured listen port for the specified listener, or -1 if
901   *          there is no such listener or the listener is not active.
902   */
903  public synchronized int getListenPort(final String listenerName)
904  {
905    final String name;
906    if (listenerName == null)
907    {
908      name = getFirstListenerName();
909    }
910    else
911    {
912      name = StaticUtils.toLowerCase(listenerName);
913    }
914
915    final LDAPListener listener = listeners.get(name);
916    if (listener == null)
917    {
918      return -1;
919    }
920    else
921    {
922      return listener.getListenPort();
923    }
924  }
925
926
927
928  /**
929   * Retrieves the configured client socket factory for the first active
930   * listener.
931   *
932   * @return  The configured client socket factory for the first active
933   *          listener, or {@code null} if that listener does not have an
934   *          explicitly-configured socket factory or there are no active
935   *          listeners.
936   */
937  public SocketFactory getClientSocketFactory()
938  {
939    return getClientSocketFactory(null);
940  }
941
942
943
944  /**
945   * Retrieves the configured client socket factory for the specified listener,
946   * if available.
947   *
948   * @param  listenerName  The name of the listener for which to retrieve the
949   *                       client socket factory.  It may be {@code null} in
950   *                       order to obtain the client socket factory for the
951   *                       first active listener.
952   *
953   * @return  The configured client socket factory for the specified listener,
954   *          or {@code null} if there is no such listener or that listener does
955   *          not have an explicitly-configured client socket factory.
956   */
957  public synchronized SocketFactory getClientSocketFactory(
958                                         final String listenerName)
959  {
960    final String name;
961    if (listenerName == null)
962    {
963      name = getFirstListenerName();
964    }
965    else
966    {
967      name = StaticUtils.toLowerCase(listenerName);
968    }
969
970    return clientSocketFactories.get(name);
971  }
972
973
974
975  /**
976   * Retrieves the name of the first running listener.
977   *
978   * @return  The name of the first running listener, or {@code null} if there
979   *          are no active listeners.
980   */
981  private String getFirstListenerName()
982  {
983    for (final Map.Entry<String,LDAPListenerConfig> e :
984         ldapListenerConfigs.entrySet())
985    {
986      final String name = e.getKey();
987      if (listeners.containsKey(name))
988      {
989        return name;
990      }
991    }
992
993    return null;
994  }
995
996
997
998  /**
999   * Retrieves the delay in milliseconds that the server should impose before
1000   * beginning processing for operations.
1001   *
1002   * @return  The delay in milliseconds that the server should impose before
1003   *          beginning processing for operations, or 0 if there should be no
1004   *          delay inserted when processing operations.
1005   */
1006  public long getProcessingDelayMillis()
1007  {
1008    return inMemoryHandler.getProcessingDelayMillis();
1009  }
1010
1011
1012
1013  /**
1014   * Specifies the delay in milliseconds that the server should impose before
1015   * beginning processing for operations.
1016   *
1017   * @param  processingDelayMillis  The delay in milliseconds that the server
1018   *                                should impose before beginning processing
1019   *                                for operations.  A value less than or equal
1020   *                                to zero may be used to indicate that there
1021   *                                should be no delay.
1022   */
1023  public void setProcessingDelayMillis(final long processingDelayMillis)
1024  {
1025    inMemoryHandler.setProcessingDelayMillis(processingDelayMillis);
1026  }
1027
1028
1029
1030  /**
1031   * Retrieves the number of entries currently held in the server.  The count
1032   * returned will not include entries which are part of the changelog.
1033   * <BR><BR>
1034   * This method may be used regardless of whether the server is listening for
1035   * client connections.
1036   *
1037   * @return  The number of entries currently held in the server.
1038   */
1039  public int countEntries()
1040  {
1041    return countEntries(false);
1042  }
1043
1044
1045
1046  /**
1047   * Retrieves the number of entries currently held in the server, optionally
1048   * including those entries which are part of the changelog.
1049   * <BR><BR>
1050   * This method may be used regardless of whether the server is listening for
1051   * client connections.
1052   *
1053   * @param  includeChangeLog  Indicates whether to include entries that are
1054   *                           part of the changelog in the count.
1055   *
1056   * @return  The number of entries currently held in the server.
1057   */
1058  public int countEntries(final boolean includeChangeLog)
1059  {
1060    return inMemoryHandler.countEntries(includeChangeLog);
1061  }
1062
1063
1064
1065  /**
1066   * Retrieves the number of entries currently held in the server whose DN
1067   * matches or is subordinate to the provided base DN.
1068   * <BR><BR>
1069   * This method may be used regardless of whether the server is listening for
1070   * client connections.
1071   *
1072   * @param  baseDN  The base DN to use for the determination.
1073   *
1074   * @return  The number of entries currently held in the server whose DN
1075   *          matches or is subordinate to the provided base DN.
1076   *
1077   * @throws  LDAPException  If the provided string cannot be parsed as a valid
1078   *                         DN.
1079   */
1080  public int countEntriesBelow(final String baseDN)
1081         throws LDAPException
1082  {
1083    return inMemoryHandler.countEntriesBelow(baseDN);
1084  }
1085
1086
1087
1088  /**
1089   * Removes all entries currently held in the server.  If a changelog is
1090   * enabled, then all changelog entries will also be cleared but the base
1091   * "cn=changelog" entry will be retained.
1092   * <BR><BR>
1093   * This method may be used regardless of whether the server is listening for
1094   * client connections.
1095   */
1096  public void clear()
1097  {
1098    inMemoryHandler.clear();
1099  }
1100
1101
1102
1103  /**
1104   * Reads entries from the specified LDIF file and adds them to the server,
1105   * optionally clearing any existing entries before beginning to add the new
1106   * entries.  If an error is encountered while adding entries from LDIF then
1107   * the server will remain populated with the data it held before the import
1108   * attempt (even if the {@code clear} is given with a value of {@code true}).
1109   * <BR><BR>
1110   * This method may be used regardless of whether the server is listening for
1111   * client connections.
1112   *
1113   * @param  clear  Indicates whether to remove all existing entries prior to
1114   *                adding entries read from LDIF.
1115   * @param  path   The path to the LDIF file from which the entries should be
1116   *                read.  It must not be {@code null}.
1117   *
1118   * @return  The number of entries read from LDIF and added to the server.
1119   *
1120   * @throws  LDAPException  If a problem occurs while reading entries or adding
1121   *                         them to the server.
1122   */
1123  public int importFromLDIF(final boolean clear, final String path)
1124         throws LDAPException
1125  {
1126    final LDIFReader reader;
1127    try
1128    {
1129      reader = new LDIFReader(path);
1130    }
1131    catch (final Exception e)
1132    {
1133      Debug.debugException(e);
1134      throw new LDAPException(ResultCode.LOCAL_ERROR,
1135           ERR_MEM_DS_INIT_FROM_LDIF_CANNOT_CREATE_READER.get(path,
1136                StaticUtils.getExceptionMessage(e)),
1137           e);
1138    }
1139
1140    return importFromLDIF(clear, reader);
1141  }
1142
1143
1144
1145  /**
1146   * Reads entries from the provided LDIF reader and adds them to the server,
1147   * optionally clearing any existing entries before beginning to add the new
1148   * entries.  If an error is encountered while adding entries from LDIF then
1149   * the server will remain populated with the data it held before the import
1150   * attempt (even if the {@code clear} is given with a value of {@code true}).
1151   * <BR><BR>
1152   * This method may be used regardless of whether the server is listening for
1153   * client connections.
1154   *
1155   * @param  clear   Indicates whether to remove all existing entries prior to
1156   *                 adding entries read from LDIF.
1157   * @param  reader  The LDIF reader to use to obtain the entries to be
1158   *                 imported.
1159   *
1160   * @return  The number of entries read from LDIF and added to the server.
1161   *
1162   * @throws  LDAPException  If a problem occurs while reading entries or adding
1163   *                         them to the server.
1164   */
1165  public int importFromLDIF(final boolean clear, final LDIFReader reader)
1166         throws LDAPException
1167  {
1168    return inMemoryHandler.importFromLDIF(clear, reader);
1169  }
1170
1171
1172
1173  /**
1174   * Writes the current contents of the server in LDIF form to the specified
1175   * file.
1176   * <BR><BR>
1177   * This method may be used regardless of whether the server is listening for
1178   * client connections.
1179   *
1180   * @param  path                   The path of the file to which the LDIF
1181   *                                entries should be written.
1182   * @param  excludeGeneratedAttrs  Indicates whether to exclude automatically
1183   *                                generated operational attributes like
1184   *                                entryUUID, entryDN, creatorsName, etc.
1185   * @param  excludeChangeLog       Indicates whether to exclude entries
1186   *                                contained in the changelog.
1187   *
1188   * @return  The number of entries written to LDIF.
1189   *
1190   * @throws  LDAPException  If a problem occurs while writing entries to LDIF.
1191   */
1192  public int exportToLDIF(final String path,
1193                          final boolean excludeGeneratedAttrs,
1194                          final boolean excludeChangeLog)
1195         throws LDAPException
1196  {
1197    final LDIFWriter ldifWriter;
1198    try
1199    {
1200      ldifWriter = new LDIFWriter(path);
1201    }
1202    catch (final Exception e)
1203    {
1204      Debug.debugException(e);
1205      throw new LDAPException(ResultCode.LOCAL_ERROR,
1206           ERR_MEM_DS_EXPORT_TO_LDIF_CANNOT_CREATE_WRITER.get(path,
1207                StaticUtils.getExceptionMessage(e)),
1208           e);
1209    }
1210
1211    return exportToLDIF(ldifWriter, excludeGeneratedAttrs, excludeChangeLog,
1212         true);
1213  }
1214
1215
1216
1217  /**
1218   * Writes the current contents of the server in LDIF form using the provided
1219   * LDIF writer.
1220   * <BR><BR>
1221   * This method may be used regardless of whether the server is listening for
1222   * client connections.
1223   *
1224   * @param  ldifWriter             The LDIF writer to use when writing the
1225   *                                entries.  It must not be {@code null}.
1226   * @param  excludeGeneratedAttrs  Indicates whether to exclude automatically
1227   *                                generated operational attributes like
1228   *                                entryUUID, entryDN, creatorsName, etc.
1229   * @param  excludeChangeLog       Indicates whether to exclude entries
1230   *                                contained in the changelog.
1231   * @param  closeWriter            Indicates whether the LDIF writer should be
1232   *                                closed after all entries have been written.
1233   *
1234   * @return  The number of entries written to LDIF.
1235   *
1236   * @throws  LDAPException  If a problem occurs while writing entries to LDIF.
1237   */
1238  public int exportToLDIF(final LDIFWriter ldifWriter,
1239                          final boolean excludeGeneratedAttrs,
1240                          final boolean excludeChangeLog,
1241                          final boolean closeWriter)
1242         throws LDAPException
1243  {
1244    return inMemoryHandler.exportToLDIF(ldifWriter, excludeGeneratedAttrs,
1245         excludeChangeLog, closeWriter);
1246  }
1247
1248
1249
1250  /**
1251   * {@inheritDoc}
1252   * <BR><BR>
1253   * This method may be used regardless of whether the server is listening for
1254   * client connections.
1255   */
1256  public RootDSE getRootDSE()
1257         throws LDAPException
1258  {
1259    return new RootDSE(inMemoryHandler.getEntry(""));
1260  }
1261
1262
1263
1264  /**
1265   * {@inheritDoc}
1266   * <BR><BR>
1267   * This method may be used regardless of whether the server is listening for
1268   * client connections.
1269   */
1270  public Schema getSchema()
1271         throws LDAPException
1272  {
1273    return inMemoryHandler.getSchema();
1274  }
1275
1276
1277
1278  /**
1279   * {@inheritDoc}
1280   * <BR><BR>
1281   * This method may be used regardless of whether the server is listening for
1282   * client connections.
1283   */
1284  public Schema getSchema(final String entryDN)
1285         throws LDAPException
1286  {
1287    return inMemoryHandler.getSchema();
1288  }
1289
1290
1291
1292  /**
1293   * {@inheritDoc}
1294   * <BR><BR>
1295   * This method may be used regardless of whether the server is listening for
1296   * client connections.
1297   */
1298  public SearchResultEntry getEntry(final String dn)
1299         throws LDAPException
1300  {
1301    return searchForEntry(dn, SearchScope.BASE,
1302         Filter.createPresenceFilter("objectClass"));
1303  }
1304
1305
1306
1307  /**
1308   * {@inheritDoc}
1309   * <BR><BR>
1310   * This method may be used regardless of whether the server is listening for
1311   * client connections, and regardless of whether search operations are
1312   * allowed in the server.
1313   */
1314  public SearchResultEntry getEntry(final String dn, final String... attributes)
1315         throws LDAPException
1316  {
1317    return searchForEntry(dn, SearchScope.BASE,
1318         Filter.createPresenceFilter("objectClass"), attributes);
1319  }
1320
1321
1322
1323  /**
1324   * {@inheritDoc}
1325   * <BR><BR>
1326   * This method may be used regardless of whether the server is listening for
1327   * client connections, and regardless of whether add operations are allowed in
1328   * the server.
1329   */
1330  public LDAPResult add(final String dn, final Attribute... attributes)
1331         throws LDAPException
1332  {
1333    return add(new AddRequest(dn, attributes));
1334  }
1335
1336
1337
1338  /**
1339   * {@inheritDoc}
1340   * <BR><BR>
1341   * This method may be used regardless of whether the server is listening for
1342   * client connections, and regardless of whether add operations are allowed in
1343   * the server.
1344   */
1345  public LDAPResult add(final String dn, final Collection<Attribute> attributes)
1346         throws LDAPException
1347  {
1348    return add(new AddRequest(dn, attributes));
1349  }
1350
1351
1352
1353  /**
1354   * {@inheritDoc}
1355   * <BR><BR>
1356   * This method may be used regardless of whether the server is listening for
1357   * client connections, and regardless of whether add operations are allowed in
1358   * the server.
1359   */
1360  public LDAPResult add(final Entry entry)
1361         throws LDAPException
1362  {
1363    return add(new AddRequest(entry));
1364  }
1365
1366
1367
1368  /**
1369   * {@inheritDoc}
1370   * <BR><BR>
1371   * This method may be used regardless of whether the server is listening for
1372   * client connections, and regardless of whether add operations are allowed in
1373   * the server.
1374   */
1375  public LDAPResult add(final String... ldifLines)
1376         throws LDIFException, LDAPException
1377  {
1378    return add(new AddRequest(ldifLines));
1379  }
1380
1381
1382
1383  /**
1384   * {@inheritDoc}
1385   * <BR><BR>
1386   * This method may be used regardless of whether the server is listening for
1387   * client connections, and regardless of whether add operations are allowed in
1388   * the server.
1389   */
1390  public LDAPResult add(final AddRequest addRequest)
1391         throws LDAPException
1392  {
1393    final ArrayList<Control> requestControlList =
1394         new ArrayList<Control>(addRequest.getControlList());
1395    requestControlList.add(new Control(
1396         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1397
1398    final LDAPMessage responseMessage = inMemoryHandler.processAddRequest(1,
1399         new AddRequestProtocolOp(addRequest.getDN(),
1400              addRequest.getAttributes()),
1401         requestControlList);
1402
1403    final AddResponseProtocolOp addResponse =
1404         responseMessage.getAddResponseProtocolOp();
1405
1406    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
1407         ResultCode.valueOf(addResponse.getResultCode()),
1408         addResponse.getDiagnosticMessage(), addResponse.getMatchedDN(),
1409         addResponse.getReferralURLs(), responseMessage.getControls());
1410
1411    switch (addResponse.getResultCode())
1412    {
1413      case ResultCode.SUCCESS_INT_VALUE:
1414      case ResultCode.NO_OPERATION_INT_VALUE:
1415        return ldapResult;
1416      default:
1417        throw new LDAPException(ldapResult);
1418    }
1419  }
1420
1421
1422
1423  /**
1424   * {@inheritDoc}
1425   * <BR><BR>
1426   * This method may be used regardless of whether the server is listening for
1427   * client connections, and regardless of whether add operations are allowed in
1428   * the server.
1429   */
1430  public LDAPResult add(final ReadOnlyAddRequest addRequest)
1431         throws LDAPException
1432  {
1433    return add(addRequest.duplicate());
1434  }
1435
1436
1437
1438  /**
1439   * Attempts to add all of the provided entries to the server.  If a problem is
1440   * encountered while attempting to add any of the provided entries, then the
1441   * server will remain populated with the data it held before this method was
1442   * called.
1443   * <BR><BR>
1444   * This method may be used regardless of whether the server is listening for
1445   * client connections, and regardless of whether add operations are allowed in
1446   * the server.
1447   *
1448   * @param  entries  The entries to be added to the server.
1449   *
1450   * @throws  LDAPException  If a problem is encountered while attempting to add
1451   *                         any of the provided entries.
1452   */
1453  public void addEntries(final Entry... entries)
1454         throws LDAPException
1455  {
1456    addEntries(Arrays.asList(entries));
1457  }
1458
1459
1460
1461  /**
1462   * Attempts to add all of the provided entries to the server.  If a problem is
1463   * encountered while attempting to add any of the provided entries, then the
1464   * server will remain populated with the data it held before this method was
1465   * called.
1466   * <BR><BR>
1467   * This method may be used regardless of whether the server is listening for
1468   * client connections, and regardless of whether add operations are allowed in
1469   * the server.
1470   *
1471   * @param  entries  The entries to be added to the server.
1472   *
1473   * @throws  LDAPException  If a problem is encountered while attempting to add
1474   *                         any of the provided entries.
1475   */
1476  public void addEntries(final List<? extends Entry> entries)
1477         throws LDAPException
1478  {
1479    inMemoryHandler.addEntries(entries);
1480  }
1481
1482
1483
1484  /**
1485   * Attempts to add a set of entries provided in LDIF form in which each
1486   * element of the provided array is a line of the LDIF representation, with
1487   * empty strings as separators between entries (as you would have for blank
1488   * lines in an LDIF file).  If a problem is encountered while attempting to
1489   * add any of the provided entries, then the server will remain populated with
1490   * the data it held before this method was called.
1491   * <BR><BR>
1492   * This method may be used regardless of whether the server is listening for
1493   * client connections, and regardless of whether add operations are allowed in
1494   * the server.
1495   *
1496   * @param  ldifEntryLines  The lines comprising the LDIF representation of the
1497   *                         entries to be added.
1498   *
1499   * @throws  LDAPException  If a problem is encountered while attempting to add
1500   *                         any of the provided entries.
1501   */
1502  public void addEntries(final String... ldifEntryLines)
1503         throws LDAPException
1504  {
1505    final ByteStringBuffer buffer = new ByteStringBuffer();
1506    for (final String line : ldifEntryLines)
1507    {
1508      buffer.append(line);
1509      buffer.append(StaticUtils.EOL_BYTES);
1510    }
1511
1512    final ArrayList<Entry> entryList = new ArrayList<Entry>(10);
1513    final LDIFReader reader = new LDIFReader(buffer.asInputStream());
1514    while (true)
1515    {
1516      try
1517      {
1518        final Entry entry = reader.readEntry();
1519        if (entry == null)
1520        {
1521          break;
1522        }
1523        else
1524        {
1525          entryList.add(entry);
1526        }
1527      }
1528      catch (final Exception e)
1529      {
1530        Debug.debugException(e);
1531        throw new LDAPException(ResultCode.PARAM_ERROR,
1532             ERR_MEM_DS_ADD_ENTRIES_LDIF_PARSE_EXCEPTION.get(
1533                  StaticUtils.getExceptionMessage(e)),
1534             e);
1535      }
1536    }
1537
1538    addEntries(entryList);
1539  }
1540
1541
1542
1543  /**
1544   * Processes a simple bind request with the provided DN and password.  Note
1545   * that the bind processing will verify that the provided credentials are
1546   * valid, but it will not alter the server in any way.
1547   *
1548   * @param  bindDN    The bind DN for the bind operation.
1549   * @param  password  The password for the simple bind operation.
1550   *
1551   * @return  The result of processing the bind operation.
1552   *
1553   * @throws  LDAPException  If the server rejects the bind request, or if a
1554   *                         problem occurs while sending the request or reading
1555   *                         the response.
1556   */
1557  public BindResult bind(final String bindDN, final String password)
1558         throws LDAPException
1559  {
1560    return bind(new SimpleBindRequest(bindDN, password));
1561  }
1562
1563
1564
1565  /**
1566   * Processes the provided bind request.  Only simple and SASL PLAIN bind
1567   * requests are supported.  Note that the bind processing will verify that the
1568   * provided credentials are valid, but it will not alter the server in any
1569   * way.
1570   *
1571   * @param  bindRequest  The bind request to be processed.  It must not be
1572   *                      {@code null}.
1573   *
1574   * @return  The result of processing the bind operation.
1575   *
1576   * @throws  LDAPException  If the server rejects the bind request, or if a
1577   *                         problem occurs while sending the request or reading
1578   *                         the response.
1579   */
1580  public BindResult bind(final BindRequest bindRequest)
1581         throws LDAPException
1582  {
1583    final ArrayList<Control> requestControlList =
1584         new ArrayList<Control>(bindRequest.getControlList());
1585    requestControlList.add(new Control(
1586         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1587
1588    final BindRequestProtocolOp bindOp;
1589    if (bindRequest instanceof SimpleBindRequest)
1590    {
1591      final SimpleBindRequest r = (SimpleBindRequest) bindRequest;
1592      bindOp = new BindRequestProtocolOp(r.getBindDN(),
1593           r.getPassword().getValue());
1594    }
1595    else if (bindRequest instanceof PLAINBindRequest)
1596    {
1597      final PLAINBindRequest r = (PLAINBindRequest) bindRequest;
1598
1599      // Create the byte array that should comprise the credentials.
1600      final byte[] authZIDBytes = StaticUtils.getBytes(r.getAuthorizationID());
1601      final byte[] authNIDBytes = StaticUtils.getBytes(r.getAuthenticationID());
1602      final byte[] passwordBytes = r.getPasswordBytes();
1603
1604      final byte[] credBytes = new byte[2 + authZIDBytes.length +
1605           authNIDBytes.length + passwordBytes.length];
1606      System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length);
1607
1608      int pos = authZIDBytes.length + 1;
1609      System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length);
1610
1611      pos += authNIDBytes.length + 1;
1612      System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length);
1613
1614      bindOp = new BindRequestProtocolOp(null, "PLAIN",
1615           new ASN1OctetString(credBytes));
1616    }
1617    else
1618    {
1619      throw new LDAPException(ResultCode.AUTH_METHOD_NOT_SUPPORTED,
1620           ERR_MEM_DS_UNSUPPORTED_BIND_TYPE.get());
1621    }
1622
1623    final LDAPMessage responseMessage = inMemoryHandler.processBindRequest(1,
1624         bindOp, requestControlList);
1625    final BindResponseProtocolOp bindResponse =
1626         responseMessage.getBindResponseProtocolOp();
1627
1628    final BindResult bindResult = new BindResult(new LDAPResult(
1629         responseMessage.getMessageID(),
1630         ResultCode.valueOf(bindResponse.getResultCode()),
1631         bindResponse.getDiagnosticMessage(), bindResponse.getMatchedDN(),
1632         bindResponse.getReferralURLs(), responseMessage.getControls()));
1633
1634    switch (bindResponse.getResultCode())
1635    {
1636      case ResultCode.SUCCESS_INT_VALUE:
1637        return bindResult;
1638      default:
1639        throw new LDAPException(bindResult);
1640    }
1641  }
1642
1643
1644
1645  /**
1646   * {@inheritDoc}
1647   * <BR><BR>
1648   * This method may be used regardless of whether the server is listening for
1649   * client connections, and regardless of whether compare operations are
1650   * allowed in the server.
1651   */
1652  public CompareResult compare(final String dn, final String attributeName,
1653                        final String assertionValue)
1654         throws LDAPException
1655  {
1656    return compare(new CompareRequest(dn, attributeName, assertionValue));
1657  }
1658
1659
1660
1661  /**
1662   * {@inheritDoc}
1663   * <BR><BR>
1664   * This method may be used regardless of whether the server is listening for
1665   * client connections, and regardless of whether compare operations are
1666   * allowed in the server.
1667   */
1668  public CompareResult compare(final CompareRequest compareRequest)
1669         throws LDAPException
1670  {
1671    final ArrayList<Control> requestControlList =
1672         new ArrayList<Control>(compareRequest.getControlList());
1673    requestControlList.add(new Control(
1674         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1675
1676    final LDAPMessage responseMessage = inMemoryHandler.processCompareRequest(1,
1677         new CompareRequestProtocolOp(compareRequest.getDN(),
1678              compareRequest.getAttributeName(),
1679              compareRequest.getRawAssertionValue()),
1680         requestControlList);
1681
1682    final CompareResponseProtocolOp compareResponse =
1683         responseMessage.getCompareResponseProtocolOp();
1684
1685    final LDAPResult compareResult = new LDAPResult(
1686         responseMessage.getMessageID(),
1687         ResultCode.valueOf(compareResponse.getResultCode()),
1688         compareResponse.getDiagnosticMessage(), compareResponse.getMatchedDN(),
1689         compareResponse.getReferralURLs(), responseMessage.getControls());
1690
1691    switch (compareResponse.getResultCode())
1692    {
1693      case ResultCode.COMPARE_TRUE_INT_VALUE:
1694      case ResultCode.COMPARE_FALSE_INT_VALUE:
1695        return new CompareResult(compareResult);
1696      default:
1697        throw new LDAPException(compareResult);
1698    }
1699  }
1700
1701
1702
1703  /**
1704   * {@inheritDoc}
1705   * <BR><BR>
1706   * This method may be used regardless of whether the server is listening for
1707   * client connections, and regardless of whether compare operations are
1708   * allowed in the server.
1709   */
1710  public CompareResult compare(final ReadOnlyCompareRequest compareRequest)
1711         throws LDAPException
1712  {
1713    return compare(compareRequest.duplicate());
1714  }
1715
1716
1717
1718  /**
1719   * {@inheritDoc}
1720   * <BR><BR>
1721   * This method may be used regardless of whether the server is listening for
1722   * client connections, and regardless of whether delete operations are
1723   * allowed in the server.
1724   */
1725  public LDAPResult delete(final String dn)
1726         throws LDAPException
1727  {
1728    return delete(new DeleteRequest(dn));
1729  }
1730
1731
1732
1733  /**
1734   * {@inheritDoc}
1735   * <BR><BR>
1736   * This method may be used regardless of whether the server is listening for
1737   * client connections, and regardless of whether delete operations are
1738   * allowed in the server.
1739   */
1740  public LDAPResult delete(final DeleteRequest deleteRequest)
1741         throws LDAPException
1742  {
1743    final ArrayList<Control> requestControlList =
1744         new ArrayList<Control>(deleteRequest.getControlList());
1745    requestControlList.add(new Control(
1746         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1747
1748    final LDAPMessage responseMessage = inMemoryHandler.processDeleteRequest(1,
1749         new DeleteRequestProtocolOp(deleteRequest.getDN()),
1750         requestControlList);
1751
1752    final DeleteResponseProtocolOp deleteResponse =
1753         responseMessage.getDeleteResponseProtocolOp();
1754
1755    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
1756         ResultCode.valueOf(deleteResponse.getResultCode()),
1757         deleteResponse.getDiagnosticMessage(), deleteResponse.getMatchedDN(),
1758         deleteResponse.getReferralURLs(), responseMessage.getControls());
1759
1760    switch (deleteResponse.getResultCode())
1761    {
1762      case ResultCode.SUCCESS_INT_VALUE:
1763      case ResultCode.NO_OPERATION_INT_VALUE:
1764        return ldapResult;
1765      default:
1766        throw new LDAPException(ldapResult);
1767    }
1768  }
1769
1770
1771
1772  /**
1773   * {@inheritDoc}
1774   * <BR><BR>
1775   * This method may be used regardless of whether the server is listening for
1776   * client connections, and regardless of whether delete operations are
1777   * allowed in the server.
1778   */
1779  public LDAPResult delete(final ReadOnlyDeleteRequest deleteRequest)
1780         throws LDAPException
1781  {
1782    return delete(deleteRequest.duplicate());
1783  }
1784
1785
1786
1787  /**
1788   * Attempts to delete the specified entry and all entries below it from the
1789   * server.
1790   * <BR><BR>
1791   * This method may be used regardless of whether the server is listening for
1792   * client connections, and regardless of whether compare operations are
1793   * allowed in the server.
1794   *
1795   * @param  baseDN  The DN of the entry to remove, along with all of its
1796   *                 subordinates.
1797   *
1798   * @return  The number of entries removed from the server, or zero if the
1799   *          specified entry was not found.
1800   *
1801   * @throws  LDAPException  If a problem is encountered while attempting to
1802   *                         remove the entries.
1803   */
1804  public int deleteSubtree(final String baseDN)
1805         throws LDAPException
1806  {
1807    return inMemoryHandler.deleteSubtree(baseDN);
1808  }
1809
1810
1811
1812  /**
1813   * Processes an extended request with the provided request OID.  Note that
1814   * because some types of extended operations return unusual result codes under
1815   * "normal" conditions, the server may not always throw an exception for a
1816   * failed extended operation like it does for other types of operations.  It
1817   * will throw an exception under conditions where there appears to be a
1818   * problem with the connection or the server to which the connection is
1819   * established, but there may be many circumstances in which an extended
1820   * operation is not processed correctly but this method does not throw an
1821   * exception.  In the event that no exception is thrown, it is the
1822   * responsibility of the caller to interpret the result to determine whether
1823   * the operation was processed as expected.
1824   * <BR><BR>
1825   * This method may be used regardless of whether the server is listening for
1826   * client connections, and regardless of whether extended operations are
1827   * allowed in the server.
1828   *
1829   * @param  requestOID  The OID for the extended request to process.  It must
1830   *                     not be {@code null}.
1831   *
1832   * @return  The extended result object that provides information about the
1833   *          result of the request processing.  It may or may not indicate that
1834   *          the operation was successful.
1835   *
1836   * @throws  LDAPException  If a problem occurs while sending the request or
1837   *                         reading the response.
1838   */
1839  public ExtendedResult processExtendedOperation(final String requestOID)
1840         throws LDAPException
1841  {
1842    Validator.ensureNotNull(requestOID);
1843
1844    return processExtendedOperation(new ExtendedRequest(requestOID));
1845  }
1846
1847
1848
1849  /**
1850   * Processes an extended request with the provided request OID and value.
1851   * Note that because some types of extended operations return unusual result
1852   * codes under "normal" conditions, the server may not always throw an
1853   * exception for a failed extended operation like it does for other types of
1854   * operations.  It will throw an exception under conditions where there
1855   * appears to be a problem with the connection or the server to which the
1856   * connection is established, but there may be many circumstances in which an
1857   * extended operation is not processed correctly but this method does not
1858   * throw an exception.  In the event that no exception is thrown, it is the
1859   * responsibility of the caller to interpret the result to determine whether
1860   * the operation was processed as expected.
1861   * <BR><BR>
1862   * This method may be used regardless of whether the server is listening for
1863   * client connections, and regardless of whether extended operations are
1864   * allowed in the server.
1865   *
1866   * @param  requestOID    The OID for the extended request to process.  It must
1867   *                       not be {@code null}.
1868   * @param  requestValue  The encoded value for the extended request to
1869   *                       process.  It may be {@code null} if there does not
1870   *                       need to be a value for the requested operation.
1871   *
1872   * @return  The extended result object that provides information about the
1873   *          result of the request processing.  It may or may not indicate that
1874   *          the operation was successful.
1875   *
1876   * @throws  LDAPException  If a problem occurs while sending the request or
1877   *                         reading the response.
1878   */
1879  public ExtendedResult processExtendedOperation(final String requestOID,
1880                             final ASN1OctetString requestValue)
1881         throws LDAPException
1882  {
1883    Validator.ensureNotNull(requestOID);
1884
1885    return processExtendedOperation(new ExtendedRequest(requestOID,
1886         requestValue));
1887  }
1888
1889
1890
1891  /**
1892   * Processes the provided extended request.  Note that because some types of
1893   * extended operations return unusual result codes under "normal" conditions,
1894   * the server may not always throw an exception for a failed extended
1895   * operation like it does for other types of operations.  It will throw an
1896   * exception under conditions where there appears to be a problem with the
1897   * connection or the server to which the connection is established, but there
1898   * may be many circumstances in which an extended operation is not processed
1899   * correctly but this method does not throw an exception.  In the event that
1900   * no exception is thrown, it is the responsibility of the caller to interpret
1901   * the result to determine whether the operation was processed as expected.
1902   * <BR><BR>
1903   * This method may be used regardless of whether the server is listening for
1904   * client connections, and regardless of whether extended operations are
1905   * allowed in the server.
1906   *
1907   * @param  extendedRequest  The extended request to be processed.  It must not
1908   *                          be {@code null}.
1909   *
1910   * @return  The extended result object that provides information about the
1911   *          result of the request processing.  It may or may not indicate that
1912   *          the operation was successful.
1913   *
1914   * @throws  LDAPException  If a problem occurs while sending the request or
1915   *                         reading the response.
1916   */
1917  public ExtendedResult processExtendedOperation(
1918                               final ExtendedRequest extendedRequest)
1919         throws LDAPException
1920  {
1921    Validator.ensureNotNull(extendedRequest);
1922
1923    final ArrayList<Control> requestControlList =
1924         new ArrayList<Control>(extendedRequest.getControlList());
1925    requestControlList.add(new Control(
1926         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1927
1928
1929    final LDAPMessage responseMessage =
1930         inMemoryHandler.processExtendedRequest(1,
1931              new ExtendedRequestProtocolOp(extendedRequest.getOID(),
1932                   extendedRequest.getValue()),
1933              requestControlList);
1934
1935    final ExtendedResponseProtocolOp extendedResponse =
1936         responseMessage.getExtendedResponseProtocolOp();
1937
1938    final ResultCode rc = ResultCode.valueOf(extendedResponse.getResultCode());
1939
1940    final String[] referralURLs;
1941    final List<String> referralURLList = extendedResponse.getReferralURLs();
1942    if ((referralURLList == null) || referralURLList.isEmpty())
1943    {
1944      referralURLs = StaticUtils.NO_STRINGS;
1945    }
1946    else
1947    {
1948      referralURLs = new String[referralURLList.size()];
1949      referralURLList.toArray(referralURLs);
1950    }
1951
1952    final Control[] responseControls;
1953    final List<Control> controlList = responseMessage.getControls();
1954    if ((controlList == null) || controlList.isEmpty())
1955    {
1956      responseControls = StaticUtils.NO_CONTROLS;
1957    }
1958    else
1959    {
1960      responseControls = new Control[controlList.size()];
1961      controlList.toArray(responseControls);
1962    }
1963
1964    final ExtendedResult extendedResult = new ExtendedResult(
1965         responseMessage.getMessageID(), rc,
1966         extendedResponse.getDiagnosticMessage(),
1967         extendedResponse.getMatchedDN(), referralURLs,
1968         extendedResponse.getResponseOID(),
1969         extendedResponse.getResponseValue(), responseControls);
1970
1971    if ((extendedResult.getOID() == null) &&
1972        (extendedResult.getValue() == null))
1973    {
1974      switch (rc.intValue())
1975      {
1976        case ResultCode.OPERATIONS_ERROR_INT_VALUE:
1977        case ResultCode.PROTOCOL_ERROR_INT_VALUE:
1978        case ResultCode.BUSY_INT_VALUE:
1979        case ResultCode.UNAVAILABLE_INT_VALUE:
1980        case ResultCode.OTHER_INT_VALUE:
1981        case ResultCode.SERVER_DOWN_INT_VALUE:
1982        case ResultCode.LOCAL_ERROR_INT_VALUE:
1983        case ResultCode.ENCODING_ERROR_INT_VALUE:
1984        case ResultCode.DECODING_ERROR_INT_VALUE:
1985        case ResultCode.TIMEOUT_INT_VALUE:
1986        case ResultCode.NO_MEMORY_INT_VALUE:
1987        case ResultCode.CONNECT_ERROR_INT_VALUE:
1988          throw new LDAPException(extendedResult);
1989      }
1990    }
1991
1992    return extendedResult;
1993  }
1994
1995
1996
1997  /**
1998   * {@inheritDoc}
1999   * <BR><BR>
2000   * This method may be used regardless of whether the server is listening for
2001   * client connections, and regardless of whether modify operations are allowed
2002   * in the server.
2003   */
2004  public LDAPResult modify(final String dn, final Modification mod)
2005         throws LDAPException
2006  {
2007    return modify(new ModifyRequest(dn, mod));
2008  }
2009
2010
2011
2012  /**
2013   * {@inheritDoc}
2014   * <BR><BR>
2015   * This method may be used regardless of whether the server is listening for
2016   * client connections, and regardless of whether modify operations are allowed
2017   * in the server.
2018   */
2019  public LDAPResult modify(final String dn, final Modification... mods)
2020         throws LDAPException
2021  {
2022    return modify(new ModifyRequest(dn, mods));
2023  }
2024
2025
2026
2027  /**
2028   * {@inheritDoc}
2029   * <BR><BR>
2030   * This method may be used regardless of whether the server is listening for
2031   * client connections, and regardless of whether modify operations are allowed
2032   * in the server.
2033   */
2034  public LDAPResult modify(final String dn, final List<Modification> mods)
2035         throws LDAPException
2036  {
2037    return modify(new ModifyRequest(dn, mods));
2038  }
2039
2040
2041
2042  /**
2043   * {@inheritDoc}
2044   * <BR><BR>
2045   * This method may be used regardless of whether the server is listening for
2046   * client connections, and regardless of whether modify operations are allowed
2047   * in the server.
2048   */
2049  public LDAPResult modify(final String... ldifModificationLines)
2050         throws LDIFException, LDAPException
2051  {
2052    return modify(new ModifyRequest(ldifModificationLines));
2053  }
2054
2055
2056
2057  /**
2058   * {@inheritDoc}
2059   * <BR><BR>
2060   * This method may be used regardless of whether the server is listening for
2061   * client connections, and regardless of whether modify operations are allowed
2062   * in the server.
2063   */
2064  public LDAPResult modify(final ModifyRequest modifyRequest)
2065         throws LDAPException
2066  {
2067    final ArrayList<Control> requestControlList =
2068         new ArrayList<Control>(modifyRequest.getControlList());
2069    requestControlList.add(new Control(
2070         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2071
2072    final LDAPMessage responseMessage = inMemoryHandler.processModifyRequest(1,
2073         new ModifyRequestProtocolOp(modifyRequest.getDN(),
2074              modifyRequest.getModifications()),
2075         requestControlList);
2076
2077    final ModifyResponseProtocolOp modifyResponse =
2078         responseMessage.getModifyResponseProtocolOp();
2079
2080    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
2081         ResultCode.valueOf(modifyResponse.getResultCode()),
2082         modifyResponse.getDiagnosticMessage(), modifyResponse.getMatchedDN(),
2083         modifyResponse.getReferralURLs(), responseMessage.getControls());
2084
2085    switch (modifyResponse.getResultCode())
2086    {
2087      case ResultCode.SUCCESS_INT_VALUE:
2088      case ResultCode.NO_OPERATION_INT_VALUE:
2089        return ldapResult;
2090      default:
2091        throw new LDAPException(ldapResult);
2092    }
2093  }
2094
2095
2096
2097  /**
2098   * {@inheritDoc}
2099   * <BR><BR>
2100   * This method may be used regardless of whether the server is listening for
2101   * client connections, and regardless of whether modify operations are allowed
2102   * in the server.
2103   */
2104  public LDAPResult modify(final ReadOnlyModifyRequest modifyRequest)
2105         throws LDAPException
2106  {
2107    return modify(modifyRequest.duplicate());
2108  }
2109
2110
2111
2112  /**
2113   * {@inheritDoc}
2114   * <BR><BR>
2115   * This method may be used regardless of whether the server is listening for
2116   * client connections, and regardless of whether modify DN operations are
2117   * allowed in the server.
2118   */
2119  public LDAPResult modifyDN(final String dn, final String newRDN,
2120                             final boolean deleteOldRDN)
2121         throws LDAPException
2122  {
2123    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
2124  }
2125
2126
2127
2128  /**
2129   * {@inheritDoc}
2130   * <BR><BR>
2131   * This method may be used regardless of whether the server is listening for
2132   * client connections, and regardless of whether modify DN operations are
2133   * allowed in the server.
2134   */
2135  public LDAPResult modifyDN(final String dn, final String newRDN,
2136                             final boolean deleteOldRDN,
2137                             final String newSuperiorDN)
2138         throws LDAPException
2139  {
2140    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
2141         newSuperiorDN));
2142  }
2143
2144
2145
2146  /**
2147   * {@inheritDoc}
2148   * <BR><BR>
2149   * This method may be used regardless of whether the server is listening for
2150   * client connections, and regardless of whether modify DN operations are
2151   * allowed in the server.
2152   */
2153  public LDAPResult modifyDN(final ModifyDNRequest modifyDNRequest)
2154         throws LDAPException
2155  {
2156    final ArrayList<Control> requestControlList =
2157         new ArrayList<Control>(modifyDNRequest.getControlList());
2158    requestControlList.add(new Control(
2159         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2160
2161    final LDAPMessage responseMessage = inMemoryHandler.processModifyDNRequest(
2162         1, new ModifyDNRequestProtocolOp(modifyDNRequest.getDN(),
2163              modifyDNRequest.getNewRDN(), modifyDNRequest.deleteOldRDN(),
2164              modifyDNRequest.getNewSuperiorDN()),
2165         requestControlList);
2166
2167    final ModifyDNResponseProtocolOp modifyDNResponse =
2168         responseMessage.getModifyDNResponseProtocolOp();
2169
2170    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
2171         ResultCode.valueOf(modifyDNResponse.getResultCode()),
2172         modifyDNResponse.getDiagnosticMessage(),
2173         modifyDNResponse.getMatchedDN(), modifyDNResponse.getReferralURLs(),
2174         responseMessage.getControls());
2175
2176    switch (modifyDNResponse.getResultCode())
2177    {
2178      case ResultCode.SUCCESS_INT_VALUE:
2179      case ResultCode.NO_OPERATION_INT_VALUE:
2180        return ldapResult;
2181      default:
2182        throw new LDAPException(ldapResult);
2183    }
2184  }
2185
2186
2187
2188  /**
2189   * {@inheritDoc}
2190   * <BR><BR>
2191   * This method may be used regardless of whether the server is listening for
2192   * client connections, and regardless of whether modify DN operations are
2193   * allowed in the server.
2194   */
2195  public LDAPResult modifyDN(final ReadOnlyModifyDNRequest modifyDNRequest)
2196         throws LDAPException
2197  {
2198    return modifyDN(modifyDNRequest.duplicate());
2199  }
2200
2201
2202
2203  /**
2204   * {@inheritDoc}
2205   * <BR><BR>
2206   * This method may be used regardless of whether the server is listening for
2207   * client connections, and regardless of whether search operations are allowed
2208   * in the server.
2209   */
2210  public SearchResult search(final String baseDN, final SearchScope scope,
2211                             final String filter, final String... attributes)
2212         throws LDAPSearchException
2213  {
2214    return search(new SearchRequest(baseDN, scope, parseFilter(filter),
2215         attributes));
2216  }
2217
2218
2219
2220  /**
2221   * {@inheritDoc}
2222   * <BR><BR>
2223   * This method may be used regardless of whether the server is listening for
2224   * client connections, and regardless of whether search operations are allowed
2225   * in the server.
2226   */
2227  public SearchResult search(final String baseDN, final SearchScope scope,
2228                             final Filter filter, final String... attributes)
2229         throws LDAPSearchException
2230  {
2231    return search(new SearchRequest(baseDN, scope, filter, attributes));
2232  }
2233
2234
2235
2236  /**
2237   * {@inheritDoc}
2238   * <BR><BR>
2239   * This method may be used regardless of whether the server is listening for
2240   * client connections, and regardless of whether search operations are allowed
2241   * in the server.
2242   */
2243  public SearchResult search(final SearchResultListener searchResultListener,
2244                             final String baseDN, final SearchScope scope,
2245                             final String filter, final String... attributes)
2246         throws LDAPSearchException
2247  {
2248    return search(new SearchRequest(searchResultListener, baseDN, scope,
2249         parseFilter(filter), attributes));
2250  }
2251
2252
2253
2254  /**
2255   * {@inheritDoc}
2256   * <BR><BR>
2257   * This method may be used regardless of whether the server is listening for
2258   * client connections, and regardless of whether search operations are allowed
2259   * in the server.
2260   */
2261  public SearchResult search(final SearchResultListener searchResultListener,
2262                             final String baseDN, final SearchScope scope,
2263                             final Filter filter, final String... attributes)
2264         throws LDAPSearchException
2265  {
2266    return search(new SearchRequest(searchResultListener, baseDN, scope,
2267         filter, attributes));
2268  }
2269
2270
2271
2272  /**
2273   * {@inheritDoc}
2274   * <BR><BR>
2275   * This method may be used regardless of whether the server is listening for
2276   * client connections, and regardless of whether search operations are allowed
2277   * in the server.
2278   */
2279  public SearchResult search(final String baseDN, final SearchScope scope,
2280                             final DereferencePolicy derefPolicy,
2281                             final int sizeLimit, final int timeLimit,
2282                             final boolean typesOnly, final String filter,
2283                             final String... attributes)
2284         throws LDAPSearchException
2285  {
2286    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2287         timeLimit, typesOnly, parseFilter(filter), attributes));
2288  }
2289
2290
2291
2292  /**
2293   * {@inheritDoc}
2294   * <BR><BR>
2295   * This method may be used regardless of whether the server is listening for
2296   * client connections, and regardless of whether search operations are allowed
2297   * in the server.
2298   */
2299  public SearchResult search(final String baseDN, final SearchScope scope,
2300                             final DereferencePolicy derefPolicy,
2301                             final int sizeLimit, final int timeLimit,
2302                             final boolean typesOnly, final Filter filter,
2303                             final String... attributes)
2304         throws LDAPSearchException
2305  {
2306    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2307         timeLimit, typesOnly, filter, attributes));
2308  }
2309
2310
2311
2312  /**
2313   * {@inheritDoc}
2314   * <BR><BR>
2315   * This method may be used regardless of whether the server is listening for
2316   * client connections, and regardless of whether search operations are allowed
2317   * in the server.
2318   */
2319  public SearchResult search(final SearchResultListener searchResultListener,
2320                             final String baseDN, final SearchScope scope,
2321                             final DereferencePolicy derefPolicy,
2322                             final int sizeLimit, final int timeLimit,
2323                             final boolean typesOnly, final String filter,
2324                             final String... attributes)
2325         throws LDAPSearchException
2326  {
2327    return search(new SearchRequest(searchResultListener, baseDN, scope,
2328         derefPolicy, sizeLimit, timeLimit, typesOnly, parseFilter(filter),
2329         attributes));
2330  }
2331
2332
2333
2334  /**
2335   * {@inheritDoc}
2336   * <BR><BR>
2337   * This method may be used regardless of whether the server is listening for
2338   * client connections, and regardless of whether search operations are allowed
2339   * in the server.
2340   */
2341  public SearchResult search(final SearchResultListener searchResultListener,
2342                             final String baseDN, final SearchScope scope,
2343                             final DereferencePolicy derefPolicy,
2344                             final int sizeLimit, final int timeLimit,
2345                             final boolean typesOnly, final Filter filter,
2346                             final String... attributes)
2347         throws LDAPSearchException
2348  {
2349    return search(new SearchRequest(searchResultListener, baseDN, scope,
2350         derefPolicy, sizeLimit, timeLimit, typesOnly, filter, attributes));
2351  }
2352
2353
2354
2355  /**
2356   * {@inheritDoc}
2357   * <BR><BR>
2358   * This method may be used regardless of whether the server is listening for
2359   * client connections, and regardless of whether search operations are allowed
2360   * in the server.
2361   */
2362  public SearchResult search(final SearchRequest searchRequest)
2363         throws LDAPSearchException
2364  {
2365    final ArrayList<Control> requestControlList =
2366         new ArrayList<Control>(searchRequest.getControlList());
2367    requestControlList.add(new Control(
2368         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2369
2370    final List<SearchResultEntry> entryList =
2371         new ArrayList<SearchResultEntry>(10);
2372    final List<SearchResultReference> referenceList =
2373         new ArrayList<SearchResultReference>(10);
2374
2375    final LDAPMessage responseMessage = inMemoryHandler.processSearchRequest(1,
2376         new SearchRequestProtocolOp(searchRequest.getBaseDN(),
2377              searchRequest.getScope(), searchRequest.getDereferencePolicy(),
2378              searchRequest.getSizeLimit(), searchRequest.getTimeLimitSeconds(),
2379              searchRequest.typesOnly(), searchRequest.getFilter(),
2380              searchRequest.getAttributeList()),
2381         requestControlList, entryList, referenceList);
2382
2383
2384    final List<SearchResultEntry> returnEntryList;
2385    final List<SearchResultReference> returnReferenceList;
2386    final SearchResultListener searchListener =
2387         searchRequest.getSearchResultListener();
2388    if (searchListener == null)
2389    {
2390      returnEntryList = Collections.unmodifiableList(entryList);
2391      returnReferenceList = Collections.unmodifiableList(referenceList);
2392    }
2393    else
2394    {
2395      returnEntryList     = null;
2396      returnReferenceList = null;
2397
2398      for (final SearchResultEntry e : entryList)
2399      {
2400        searchListener.searchEntryReturned(e);
2401      }
2402
2403      for (final SearchResultReference r : referenceList)
2404      {
2405        searchListener.searchReferenceReturned(r);
2406      }
2407    }
2408
2409
2410    final SearchResultDoneProtocolOp searchDone =
2411         responseMessage.getSearchResultDoneProtocolOp();
2412
2413    final ResultCode rc = ResultCode.valueOf(searchDone.getResultCode());
2414
2415    final String[] referralURLs;
2416    final List<String> referralURLList = searchDone.getReferralURLs();
2417    if ((referralURLList == null) || referralURLList.isEmpty())
2418    {
2419      referralURLs = StaticUtils.NO_STRINGS;
2420    }
2421    else
2422    {
2423      referralURLs = new String[referralURLList.size()];
2424      referralURLList.toArray(referralURLs);
2425    }
2426
2427    final Control[] responseControls;
2428    final List<Control> controlList = responseMessage.getControls();
2429    if ((controlList == null) || controlList.isEmpty())
2430    {
2431      responseControls = StaticUtils.NO_CONTROLS;
2432    }
2433    else
2434    {
2435      responseControls = new Control[controlList.size()];
2436      controlList.toArray(responseControls);
2437    }
2438
2439    final SearchResult searchResult =new SearchResult(
2440         responseMessage.getMessageID(), rc, searchDone.getDiagnosticMessage(),
2441         searchDone.getMatchedDN(), referralURLs, returnEntryList,
2442         returnReferenceList, entryList.size(), referenceList.size(),
2443         responseControls);
2444
2445    if (rc == ResultCode.SUCCESS)
2446    {
2447      return searchResult;
2448    }
2449    else
2450    {
2451      throw new LDAPSearchException(searchResult);
2452    }
2453  }
2454
2455
2456
2457  /**
2458   * {@inheritDoc}
2459   * <BR><BR>
2460   * This method may be used regardless of whether the server is listening for
2461   * client connections, and regardless of whether search operations are allowed
2462   * in the server.
2463   */
2464  public SearchResult search(final ReadOnlySearchRequest searchRequest)
2465         throws LDAPSearchException
2466  {
2467    return search(searchRequest.duplicate());
2468  }
2469
2470
2471
2472  /**
2473   * {@inheritDoc}
2474   * <BR><BR>
2475   * This method may be used regardless of whether the server is listening for
2476   * client connections, and regardless of whether search operations are allowed
2477   * in the server.
2478   */
2479  public SearchResultEntry searchForEntry(final String baseDN,
2480                                          final SearchScope scope,
2481                                          final String filter,
2482                                          final String... attributes)
2483         throws LDAPSearchException
2484  {
2485    return searchForEntry(new SearchRequest(baseDN, scope, parseFilter(filter),
2486         attributes));
2487  }
2488
2489
2490
2491  /**
2492   * {@inheritDoc}
2493   * <BR><BR>
2494   * This method may be used regardless of whether the server is listening for
2495   * client connections, and regardless of whether search operations are allowed
2496   * in the server.
2497   */
2498  public SearchResultEntry searchForEntry(final String baseDN,
2499                                          final SearchScope scope,
2500                                          final Filter filter,
2501                                          final String... attributes)
2502         throws LDAPSearchException
2503  {
2504    return searchForEntry(new SearchRequest(baseDN, scope, filter, attributes));
2505  }
2506
2507
2508
2509  /**
2510   * {@inheritDoc}
2511   * <BR><BR>
2512   * This method may be used regardless of whether the server is listening for
2513   * client connections, and regardless of whether search operations are allowed
2514   * in the server.
2515   */
2516  public SearchResultEntry searchForEntry(final String baseDN,
2517                                          final SearchScope scope,
2518                                          final DereferencePolicy derefPolicy,
2519                                          final int timeLimit,
2520                                          final boolean typesOnly,
2521                                          final String filter,
2522                                          final String... attributes)
2523         throws LDAPSearchException
2524  {
2525    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2526         timeLimit, typesOnly, parseFilter(filter), attributes));
2527  }
2528
2529
2530
2531  /**
2532   * {@inheritDoc}
2533   * <BR><BR>
2534   * This method may be used regardless of whether the server is listening for
2535   * client connections, and regardless of whether search operations are allowed
2536   * in the server.
2537   */
2538  public SearchResultEntry searchForEntry(final String baseDN,
2539                                          final SearchScope scope,
2540                                          final DereferencePolicy derefPolicy,
2541                                          final int timeLimit,
2542                                          final boolean typesOnly,
2543                                          final Filter filter,
2544                                          final String... attributes)
2545         throws LDAPSearchException
2546  {
2547    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2548         timeLimit, typesOnly, filter, attributes));
2549  }
2550
2551
2552
2553  /**
2554   * {@inheritDoc}
2555   * <BR><BR>
2556   * This method may be used regardless of whether the server is listening for
2557   * client connections, and regardless of whether search operations are allowed
2558   * in the server.
2559   */
2560  public SearchResultEntry searchForEntry(final SearchRequest searchRequest)
2561         throws LDAPSearchException
2562  {
2563    final ArrayList<Control> requestControlList =
2564         new ArrayList<Control>(searchRequest.getControlList());
2565    requestControlList.add(new Control(
2566         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2567
2568    final SearchRequest r;
2569    if ((searchRequest.getSizeLimit() == 1) &&
2570        (searchRequest.getSearchResultListener() == null))
2571    {
2572      r = searchRequest;
2573    }
2574    else
2575    {
2576      r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(),
2577           searchRequest.getDereferencePolicy(), 1,
2578           searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(),
2579           searchRequest.getFilter(), searchRequest.getAttributes());
2580
2581      r.setFollowReferrals(InternalSDKHelper.followReferralsInternal(r));
2582      r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null));
2583      r.setControls(requestControlList);
2584    }
2585
2586    final SearchResult result;
2587    try
2588    {
2589      result = search(r);
2590    }
2591    catch (final LDAPSearchException lse)
2592    {
2593      Debug.debugException(lse);
2594
2595      if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT)
2596      {
2597        return null;
2598      }
2599
2600      throw lse;
2601    }
2602
2603    if (result.getEntryCount() == 0)
2604    {
2605      return null;
2606    }
2607    else
2608    {
2609      return result.getSearchEntries().get(0);
2610    }
2611  }
2612
2613
2614
2615  /**
2616   * {@inheritDoc}
2617   * <BR><BR>
2618   * This method may be used regardless of whether the server is listening for
2619   * client connections, and regardless of whether search operations are allowed
2620   * in the server.
2621   */
2622  public SearchResultEntry searchForEntry(
2623                                final ReadOnlySearchRequest searchRequest)
2624         throws LDAPSearchException
2625  {
2626    return searchForEntry(searchRequest.duplicate());
2627  }
2628
2629
2630
2631  /**
2632   * Parses the provided string as a search filter.
2633   *
2634   * @param  s  The string to be parsed.
2635   *
2636   * @return  The parsed filter.
2637   *
2638   * @throws  LDAPSearchException  If the provided string could not be parsed as
2639   *                               a valid search filter.
2640   */
2641  private static Filter parseFilter(final String s)
2642          throws LDAPSearchException
2643  {
2644    try
2645    {
2646      return Filter.create(s);
2647    }
2648    catch (final LDAPException le)
2649    {
2650      throw new LDAPSearchException(le);
2651    }
2652  }
2653
2654
2655
2656  /**
2657   * Indicates whether the specified entry exists in the server.
2658   * <BR><BR>
2659   * This method may be used regardless of whether the server is listening for
2660   * client connections.
2661   *
2662   * @param  dn  The DN of the entry for which to make the determination.
2663   *
2664   * @return  {@code true} if the entry exists, or {@code false} if not.
2665   *
2666   * @throws  LDAPException  If a problem is encountered while trying to
2667   *                         communicate with the directory server.
2668   */
2669  public boolean entryExists(final String dn)
2670         throws LDAPException
2671  {
2672    return inMemoryHandler.entryExists(dn);
2673  }
2674
2675
2676
2677  /**
2678   * Indicates whether the specified entry exists in the server and matches the
2679   * given filter.
2680   * <BR><BR>
2681   * This method may be used regardless of whether the server is listening for
2682   * client connections.
2683   *
2684   * @param  dn      The DN of the entry for which to make the determination.
2685   * @param  filter  The filter the entry is expected to match.
2686   *
2687   * @return  {@code true} if the entry exists and matches the specified filter,
2688   *          or {@code false} if not.
2689   *
2690   * @throws  LDAPException  If a problem is encountered while trying to
2691   *                         communicate with the directory server.
2692   */
2693  public boolean entryExists(final String dn, final String filter)
2694         throws LDAPException
2695  {
2696    return inMemoryHandler.entryExists(dn, filter);
2697  }
2698
2699
2700
2701  /**
2702   * Indicates whether the specified entry exists in the server.  This will
2703   * return {@code true} only if the target entry exists and contains all values
2704   * for all attributes of the provided entry.  The entry will be allowed to
2705   * have attribute values not included in the provided entry.
2706   * <BR><BR>
2707   * This method may be used regardless of whether the server is listening for
2708   * client connections.
2709   *
2710   * @param  entry  The entry to compare against the directory server.
2711   *
2712   * @return  {@code true} if the entry exists in the server and is a superset
2713   *          of the provided entry, or {@code false} if not.
2714   *
2715   * @throws  LDAPException  If a problem is encountered while trying to
2716   *                         communicate with the directory server.
2717   */
2718  public boolean entryExists(final Entry entry)
2719         throws LDAPException
2720  {
2721    return inMemoryHandler.entryExists(entry);
2722  }
2723
2724
2725
2726  /**
2727   * Ensures that an entry with the provided DN exists in the directory.
2728   * <BR><BR>
2729   * This method may be used regardless of whether the server is listening for
2730   * client connections.
2731   *
2732   * @param  dn  The DN of the entry for which to make the determination.
2733   *
2734   * @throws  LDAPException  If a problem is encountered while trying to
2735   *                         communicate with the directory server.
2736   *
2737   * @throws  AssertionError  If the target entry does not exist.
2738   */
2739  public void assertEntryExists(final String dn)
2740         throws LDAPException, AssertionError
2741  {
2742    inMemoryHandler.assertEntryExists(dn);
2743  }
2744
2745
2746
2747  /**
2748   * Ensures that an entry with the provided DN exists in the directory.
2749   * <BR><BR>
2750   * This method may be used regardless of whether the server is listening for
2751   * client connections.
2752   *
2753   * @param  dn      The DN of the entry for which to make the determination.
2754   * @param  filter  A filter that the target entry must match.
2755   *
2756   * @throws  LDAPException  If a problem is encountered while trying to
2757   *                         communicate with the directory server.
2758   *
2759   * @throws  AssertionError  If the target entry does not exist or does not
2760   *                          match the provided filter.
2761   */
2762  public void assertEntryExists(final String dn, final String filter)
2763         throws LDAPException, AssertionError
2764  {
2765    inMemoryHandler.assertEntryExists(dn, filter);
2766  }
2767
2768
2769
2770  /**
2771   * Ensures that an entry exists in the directory with the same DN and all
2772   * attribute values contained in the provided entry.  The server entry may
2773   * contain additional attributes and/or attribute values not included in the
2774   * provided entry.
2775   * <BR><BR>
2776   * This method may be used regardless of whether the server is listening for
2777   * client connections.
2778   *
2779   * @param  entry  The entry expected to be present in the directory server.
2780   *
2781   * @throws  LDAPException  If a problem is encountered while trying to
2782   *                         communicate with the directory server.
2783   *
2784   * @throws  AssertionError  If the target entry does not exist or does not
2785   *                          match the provided filter.
2786   */
2787  public void assertEntryExists(final Entry entry)
2788         throws LDAPException, AssertionError
2789  {
2790    inMemoryHandler.assertEntryExists(entry);
2791  }
2792
2793
2794
2795  /**
2796   * Retrieves a list containing the DNs of the entries which are missing from
2797   * the directory server.
2798   * <BR><BR>
2799   * This method may be used regardless of whether the server is listening for
2800   * client connections.
2801   *
2802   * @param  dns  The DNs of the entries to try to find in the server.
2803   *
2804   * @return  A list containing all of the provided DNs that were not found in
2805   *          the server, or an empty list if all entries were found.
2806   *
2807   * @throws  LDAPException  If a problem is encountered while trying to
2808   *                         communicate with the directory server.
2809   */
2810  public List<String> getMissingEntryDNs(final String... dns)
2811         throws LDAPException
2812  {
2813    return inMemoryHandler.getMissingEntryDNs(StaticUtils.toList(dns));
2814  }
2815
2816
2817
2818  /**
2819   * Retrieves a list containing the DNs of the entries which are missing from
2820   * the directory server.
2821   * <BR><BR>
2822   * This method may be used regardless of whether the server is listening for
2823   * client connections.
2824   *
2825   * @param  dns  The DNs of the entries to try to find in the server.
2826   *
2827   * @return  A list containing all of the provided DNs that were not found in
2828   *          the server, or an empty list if all entries were found.
2829   *
2830   * @throws  LDAPException  If a problem is encountered while trying to
2831   *                         communicate with the directory server.
2832   */
2833  public List<String> getMissingEntryDNs(final Collection<String> dns)
2834         throws LDAPException
2835  {
2836    return inMemoryHandler.getMissingEntryDNs(dns);
2837  }
2838
2839
2840
2841  /**
2842   * Ensures that all of the entries with the provided DNs exist in the
2843   * directory.
2844   * <BR><BR>
2845   * This method may be used regardless of whether the server is listening for
2846   * client connections.
2847   *
2848   * @param  dns  The DNs of the entries for which to make the determination.
2849   *
2850   * @throws  LDAPException  If a problem is encountered while trying to
2851   *                         communicate with the directory server.
2852   *
2853   * @throws  AssertionError  If any of the target entries does not exist.
2854   */
2855  public void assertEntriesExist(final String... dns)
2856         throws LDAPException, AssertionError
2857  {
2858    inMemoryHandler.assertEntriesExist(StaticUtils.toList(dns));
2859  }
2860
2861
2862
2863  /**
2864   * Ensures that all of the entries with the provided DNs exist in the
2865   * directory.
2866   * <BR><BR>
2867   * This method may be used regardless of whether the server is listening for
2868   * client connections.
2869   *
2870   * @param  dns  The DNs of the entries for which to make the determination.
2871   *
2872   * @throws  LDAPException  If a problem is encountered while trying to
2873   *                         communicate with the directory server.
2874   *
2875   * @throws  AssertionError  If any of the target entries does not exist.
2876   */
2877  public void assertEntriesExist(final Collection<String> dns)
2878         throws LDAPException, AssertionError
2879  {
2880    inMemoryHandler.assertEntriesExist(dns);
2881  }
2882
2883
2884
2885  /**
2886   * Retrieves a list containing all of the named attributes which do not exist
2887   * in the target entry.
2888   * <BR><BR>
2889   * This method may be used regardless of whether the server is listening for
2890   * client connections.
2891   *
2892   * @param  dn              The DN of the entry to examine.
2893   * @param  attributeNames  The names of the attributes expected to be present
2894   *                         in the target entry.
2895   *
2896   * @return  A list containing the names of the attributes which were not
2897   *          present in the target entry, an empty list if all specified
2898   *          attributes were found in the entry, or {@code null} if the target
2899   *          entry does not exist.
2900   *
2901   * @throws  LDAPException  If a problem is encountered while trying to
2902   *                         communicate with the directory server.
2903   */
2904  public List<String> getMissingAttributeNames(final String dn,
2905                                               final String... attributeNames)
2906         throws LDAPException
2907  {
2908    return inMemoryHandler.getMissingAttributeNames(dn,
2909         StaticUtils.toList(attributeNames));
2910  }
2911
2912
2913
2914  /**
2915   * Retrieves a list containing all of the named attributes which do not exist
2916   * in the target entry.
2917   * <BR><BR>
2918   * This method may be used regardless of whether the server is listening for
2919   * client connections.
2920   *
2921   * @param  dn              The DN of the entry to examine.
2922   * @param  attributeNames  The names of the attributes expected to be present
2923   *                         in the target entry.
2924   *
2925   * @return  A list containing the names of the attributes which were not
2926   *          present in the target entry, an empty list if all specified
2927   *          attributes were found in the entry, or {@code null} if the target
2928   *          entry does not exist.
2929   *
2930   * @throws  LDAPException  If a problem is encountered while trying to
2931   *                         communicate with the directory server.
2932   */
2933  public List<String> getMissingAttributeNames(final String dn,
2934                           final Collection<String> attributeNames)
2935         throws LDAPException
2936  {
2937    return inMemoryHandler.getMissingAttributeNames(dn, attributeNames);
2938  }
2939
2940
2941
2942  /**
2943   * Ensures that the specified entry exists in the directory with all of the
2944   * specified attributes.
2945   * <BR><BR>
2946   * This method may be used regardless of whether the server is listening for
2947   * client connections.
2948   *
2949   * @param  dn              The DN of the entry to examine.
2950   * @param  attributeNames  The names of the attributes that are expected to be
2951   *                         present in the provided entry.
2952   *
2953   * @throws  LDAPException  If a problem is encountered while trying to
2954   *                         communicate with the directory server.
2955   *
2956   * @throws  AssertionError  If the target entry does not exist or does not
2957   *                          contain all of the specified attributes.
2958   */
2959  public void assertAttributeExists(final String dn,
2960                                    final String... attributeNames)
2961        throws LDAPException, AssertionError
2962  {
2963    inMemoryHandler.assertAttributeExists(dn,
2964         StaticUtils.toList(attributeNames));
2965  }
2966
2967
2968
2969  /**
2970   * Ensures that the specified entry exists in the directory with all of the
2971   * specified attributes.
2972   * <BR><BR>
2973   * This method may be used regardless of whether the server is listening for
2974   * client connections.
2975   *
2976   * @param  dn              The DN of the entry to examine.
2977   * @param  attributeNames  The names of the attributes that are expected to be
2978   *                         present in the provided entry.
2979   *
2980   * @throws  LDAPException  If a problem is encountered while trying to
2981   *                         communicate with the directory server.
2982   *
2983   * @throws  AssertionError  If the target entry does not exist or does not
2984   *                          contain all of the specified attributes.
2985   */
2986  public void assertAttributeExists(final String dn,
2987                                    final Collection<String> attributeNames)
2988        throws LDAPException, AssertionError
2989  {
2990    inMemoryHandler.assertAttributeExists(dn, attributeNames);
2991  }
2992
2993
2994
2995  /**
2996   * Retrieves a list of all provided attribute values which are missing from
2997   * the specified entry.
2998   * <BR><BR>
2999   * This method may be used regardless of whether the server is listening for
3000   * client connections.
3001   *
3002   * @param  dn               The DN of the entry to examine.
3003   * @param  attributeName    The attribute expected to be present in the target
3004   *                          entry with the given values.
3005   * @param  attributeValues  The values expected to be present in the target
3006   *                          entry.
3007   *
3008   * @return  A list containing all of the provided values which were not found
3009   *          in the entry, an empty list if all provided attribute values were
3010   *          found, or {@code null} if the target entry does not exist.
3011   *
3012   * @throws  LDAPException  If a problem is encountered while trying to
3013   *                         communicate with the directory server.
3014   */
3015  public List<String> getMissingAttributeValues(final String dn,
3016                                                final String attributeName,
3017                                                final String... attributeValues)
3018         throws LDAPException
3019  {
3020    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3021         StaticUtils.toList(attributeValues));
3022  }
3023
3024
3025
3026  /**
3027   * Retrieves a list of all provided attribute values which are missing from
3028   * the specified entry.  The target attribute may or may not contain
3029   * additional values.
3030   * <BR><BR>
3031   * This method may be used regardless of whether the server is listening for
3032   * client connections.
3033   *
3034   * @param  dn               The DN of the entry to examine.
3035   * @param  attributeName    The attribute expected to be present in the target
3036   *                          entry with the given values.
3037   * @param  attributeValues  The values expected to be present in the target
3038   *                          entry.
3039   *
3040   * @return  A list containing all of the provided values which were not found
3041   *          in the entry, an empty list if all provided attribute values were
3042   *          found, or {@code null} if the target entry does not exist.
3043   *
3044   * @throws  LDAPException  If a problem is encountered while trying to
3045   *                         communicate with the directory server.
3046   */
3047  public List<String> getMissingAttributeValues(final String dn,
3048                           final String attributeName,
3049                           final Collection<String> attributeValues)
3050       throws LDAPException
3051  {
3052    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3053         attributeValues);
3054  }
3055
3056
3057
3058  /**
3059   * Ensures that the specified entry exists in the directory with all of the
3060   * specified values for the given attribute.  The attribute may or may not
3061   * contain additional values.
3062   * <BR><BR>
3063   * This method may be used regardless of whether the server is listening for
3064   * client connections.
3065   *
3066   * @param  dn               The DN of the entry to examine.
3067   * @param  attributeName    The name of the attribute to examine.
3068   * @param  attributeValues  The set of values which must exist for the given
3069   *                          attribute.
3070   *
3071   * @throws  LDAPException  If a problem is encountered while trying to
3072   *                         communicate with the directory server.
3073   *
3074   * @throws  AssertionError  If the target entry does not exist, does not
3075   *                          contain the specified attribute, or that attribute
3076   *                          does not have all of the specified values.
3077   */
3078  public void assertValueExists(final String dn, final String attributeName,
3079                                final String... attributeValues)
3080        throws LDAPException, AssertionError
3081  {
3082    inMemoryHandler.assertValueExists(dn, attributeName,
3083         StaticUtils.toList(attributeValues));
3084  }
3085
3086
3087
3088  /**
3089   * Ensures that the specified entry exists in the directory with all of the
3090   * specified values for the given attribute.  The attribute may or may not
3091   * contain additional values.
3092   * <BR><BR>
3093   * This method may be used regardless of whether the server is listening for
3094   * client connections.
3095   *
3096   * @param  dn               The DN of the entry to examine.
3097   * @param  attributeName    The name of the attribute to examine.
3098   * @param  attributeValues  The set of values which must exist for the given
3099   *                          attribute.
3100   *
3101   * @throws  LDAPException  If a problem is encountered while trying to
3102   *                         communicate with the directory server.
3103   *
3104   * @throws  AssertionError  If the target entry does not exist, does not
3105   *                          contain the specified attribute, or that attribute
3106   *                          does not have all of the specified values.
3107   */
3108  public void assertValueExists(final String dn, final String attributeName,
3109                                final Collection<String> attributeValues)
3110        throws LDAPException, AssertionError
3111  {
3112    inMemoryHandler.assertValueExists(dn, attributeName, attributeValues);
3113  }
3114
3115
3116
3117  /**
3118   * Ensures that the specified entry does not exist in the directory.
3119   * <BR><BR>
3120   * This method may be used regardless of whether the server is listening for
3121   * client connections.
3122   *
3123   * @param  dn  The DN of the entry expected to be missing.
3124   *
3125   * @throws  LDAPException  If a problem is encountered while trying to
3126   *                         communicate with the directory server.
3127   *
3128   * @throws  AssertionError  If the target entry is found in the server.
3129   */
3130  public void assertEntryMissing(final String dn)
3131         throws LDAPException, AssertionError
3132  {
3133    inMemoryHandler.assertEntryMissing(dn);
3134  }
3135
3136
3137
3138  /**
3139   * Ensures that the specified entry exists in the directory but does not
3140   * contain any of the specified attributes.
3141   * <BR><BR>
3142   * This method may be used regardless of whether the server is listening for
3143   * client connections.
3144   *
3145   * @param  dn              The DN of the entry expected to be present.
3146   * @param  attributeNames  The names of the attributes expected to be missing
3147   *                         from the entry.
3148   *
3149   * @throws  LDAPException  If a problem is encountered while trying to
3150   *                         communicate with the directory server.
3151   *
3152   * @throws  AssertionError  If the target entry is missing from the server, or
3153   *                          if it contains any of the target attributes.
3154   */
3155  public void assertAttributeMissing(final String dn,
3156                                     final String... attributeNames)
3157         throws LDAPException, AssertionError
3158  {
3159    inMemoryHandler.assertAttributeMissing(dn,
3160         StaticUtils.toList(attributeNames));
3161  }
3162
3163
3164
3165  /**
3166   * Ensures that the specified entry exists in the directory but does not
3167   * contain any of the specified attributes.
3168   * <BR><BR>
3169   * This method may be used regardless of whether the server is listening for
3170   * client connections.
3171   *
3172   * @param  dn              The DN of the entry expected to be present.
3173   * @param  attributeNames  The names of the attributes expected to be missing
3174   *                         from the entry.
3175   *
3176   * @throws  LDAPException  If a problem is encountered while trying to
3177   *                         communicate with the directory server.
3178   *
3179   * @throws  AssertionError  If the target entry is missing from the server, or
3180   *                          if it contains any of the target attributes.
3181   */
3182  public void assertAttributeMissing(final String dn,
3183                                     final Collection<String> attributeNames)
3184         throws LDAPException, AssertionError
3185  {
3186    inMemoryHandler.assertAttributeMissing(dn, attributeNames);
3187  }
3188
3189
3190
3191  /**
3192   * Ensures that the specified entry exists in the directory but does not
3193   * contain any of the specified attribute values.
3194   * <BR><BR>
3195   * This method may be used regardless of whether the server is listening for
3196   * client connections.
3197   *
3198   * @param  dn               The DN of the entry expected to be present.
3199   * @param  attributeName    The name of the attribute to examine.
3200   * @param  attributeValues  The values expected to be missing from the target
3201   *                          entry.
3202   *
3203   * @throws  LDAPException  If a problem is encountered while trying to
3204   *                         communicate with the directory server.
3205   *
3206   * @throws  AssertionError  If the target entry is missing from the server, or
3207   *                          if it contains any of the target attribute values.
3208   */
3209  public void assertValueMissing(final String dn, final String attributeName,
3210                                 final String... attributeValues)
3211         throws LDAPException, AssertionError
3212  {
3213    inMemoryHandler.assertValueMissing(dn, attributeName,
3214         StaticUtils.toList(attributeValues));
3215  }
3216
3217
3218
3219  /**
3220   * Ensures that the specified entry exists in the directory but does not
3221   * contain any of the specified attribute values.
3222   * <BR><BR>
3223   * This method may be used regardless of whether the server is listening for
3224   * client connections.
3225   *
3226   * @param  dn               The DN of the entry expected to be present.
3227   * @param  attributeName    The name of the attribute to examine.
3228   * @param  attributeValues  The values expected to be missing from the target
3229   *                          entry.
3230   *
3231   * @throws  LDAPException  If a problem is encountered while trying to
3232   *                         communicate with the directory server.
3233   *
3234   * @throws  AssertionError  If the target entry is missing from the server, or
3235   *                          if it contains any of the target attribute values.
3236   */
3237  public void assertValueMissing(final String dn, final String attributeName,
3238                                 final Collection<String> attributeValues)
3239         throws LDAPException, AssertionError
3240  {
3241    inMemoryHandler.assertValueMissing(dn, attributeName, attributeValues);
3242  }
3243}