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