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