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