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