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