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