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