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