001/*
002 * Copyright 2011-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2011-2024 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-2024 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, Filter.createANDFilter());
1583  }
1584
1585
1586
1587  /**
1588   * {@inheritDoc}
1589   * <BR><BR>
1590   * This method may be used regardless of whether the server is listening for
1591   * client connections, and regardless of whether search operations are
1592   * allowed in the server.
1593   */
1594  @Override()
1595  @Nullable()
1596  public SearchResultEntry getEntry(@NotNull final String dn,
1597                                    @Nullable final String... attributes)
1598         throws LDAPException
1599  {
1600    return searchForEntry(dn, SearchScope.BASE, Filter.createANDFilter(),
1601         attributes);
1602  }
1603
1604
1605
1606  /**
1607   * {@inheritDoc}
1608   * <BR><BR>
1609   * This method may be used regardless of whether the server is listening for
1610   * client connections, and regardless of whether add operations are allowed in
1611   * the server.
1612   */
1613  @Override()
1614  @NotNull()
1615  public LDAPResult add(@NotNull final String dn,
1616                        @NotNull final Attribute... attributes)
1617         throws LDAPException
1618  {
1619    return add(new AddRequest(dn, attributes));
1620  }
1621
1622
1623
1624  /**
1625   * {@inheritDoc}
1626   * <BR><BR>
1627   * This method may be used regardless of whether the server is listening for
1628   * client connections, and regardless of whether add operations are allowed in
1629   * the server.
1630   */
1631  @Override()
1632  @NotNull()
1633  public LDAPResult add(@NotNull final String dn,
1634                        @NotNull final Collection<Attribute> attributes)
1635         throws LDAPException
1636  {
1637    return add(new AddRequest(dn, attributes));
1638  }
1639
1640
1641
1642  /**
1643   * {@inheritDoc}
1644   * <BR><BR>
1645   * This method may be used regardless of whether the server is listening for
1646   * client connections, and regardless of whether add operations are allowed in
1647   * the server.
1648   */
1649  @Override()
1650  @NotNull()
1651  public LDAPResult add(@NotNull final Entry entry)
1652         throws LDAPException
1653  {
1654    return add(new AddRequest(entry));
1655  }
1656
1657
1658
1659  /**
1660   * {@inheritDoc}
1661   * <BR><BR>
1662   * This method may be used regardless of whether the server is listening for
1663   * client connections, and regardless of whether add operations are allowed in
1664   * the server.
1665   */
1666  @Override()
1667  @NotNull()
1668  public LDAPResult add(@NotNull final String... ldifLines)
1669         throws LDIFException, LDAPException
1670  {
1671    return add(new AddRequest(ldifLines));
1672  }
1673
1674
1675
1676  /**
1677   * {@inheritDoc}
1678   * <BR><BR>
1679   * This method may be used regardless of whether the server is listening for
1680   * client connections, and regardless of whether add operations are allowed in
1681   * the server.
1682   */
1683  @Override()
1684  @NotNull()
1685  public LDAPResult add(@NotNull final AddRequest addRequest)
1686         throws LDAPException
1687  {
1688    return inMemoryHandler.add(addRequest);
1689  }
1690
1691
1692
1693  /**
1694   * {@inheritDoc}
1695   * <BR><BR>
1696   * This method may be used regardless of whether the server is listening for
1697   * client connections, and regardless of whether add operations are allowed in
1698   * the server.
1699   */
1700  @Override()
1701  @NotNull()
1702  public LDAPResult add(@NotNull final ReadOnlyAddRequest addRequest)
1703         throws LDAPException
1704  {
1705    return add(addRequest.duplicate());
1706  }
1707
1708
1709
1710  /**
1711   * Attempts to add all of the provided entries to the server.  If a problem is
1712   * encountered while attempting to add any of the provided entries, then the
1713   * server will remain populated with the data it held before this method was
1714   * called.
1715   * <BR><BR>
1716   * This method may be used regardless of whether the server is listening for
1717   * client connections, and regardless of whether add operations are allowed in
1718   * the server.
1719   *
1720   * @param  entries  The entries to be added to the server.
1721   *
1722   * @throws  LDAPException  If a problem is encountered while attempting to add
1723   *                         any of the provided entries.
1724   */
1725  public void addEntries(@NotNull final Entry... entries)
1726         throws LDAPException
1727  {
1728    addEntries(Arrays.asList(entries));
1729  }
1730
1731
1732
1733  /**
1734   * Attempts to add all of the provided entries to the server.  If a problem is
1735   * encountered while attempting to add any of the provided entries, then the
1736   * server will remain populated with the data it held before this method was
1737   * called.
1738   * <BR><BR>
1739   * This method may be used regardless of whether the server is listening for
1740   * client connections, and regardless of whether add operations are allowed in
1741   * the server.
1742   *
1743   * @param  entries  The entries to be added to the server.
1744   *
1745   * @throws  LDAPException  If a problem is encountered while attempting to add
1746   *                         any of the provided entries.
1747   */
1748  public void addEntries(@NotNull final List<? extends Entry> entries)
1749         throws LDAPException
1750  {
1751    inMemoryHandler.addEntries(entries);
1752  }
1753
1754
1755
1756  /**
1757   * Attempts to add a set of entries provided in LDIF form in which each
1758   * element of the provided array is a line of the LDIF representation, with
1759   * empty strings as separators between entries (as you would have for blank
1760   * lines in an LDIF file).  If a problem is encountered while attempting to
1761   * add any of the provided entries, then the server will remain populated with
1762   * the data it held before this method was called.
1763   * <BR><BR>
1764   * This method may be used regardless of whether the server is listening for
1765   * client connections, and regardless of whether add operations are allowed in
1766   * the server.
1767   *
1768   * @param  ldifEntryLines  The lines comprising the LDIF representation of the
1769   *                         entries to be added.
1770   *
1771   * @throws  LDAPException  If a problem is encountered while attempting to add
1772   *                         any of the provided entries.
1773   */
1774  public void addEntries(@NotNull final String... ldifEntryLines)
1775         throws LDAPException
1776  {
1777    final ByteStringBuffer buffer = new ByteStringBuffer();
1778    for (final String line : ldifEntryLines)
1779    {
1780      buffer.append(line);
1781      buffer.append(StaticUtils.EOL_BYTES);
1782    }
1783
1784    final ArrayList<Entry> entryList = new ArrayList<>(10);
1785    final LDIFReader reader = new LDIFReader(buffer.asInputStream());
1786
1787    final Schema schema = getSchema();
1788    if (schema != null)
1789    {
1790      reader.setSchema(schema);
1791    }
1792
1793    while (true)
1794    {
1795      try
1796      {
1797        final Entry entry = reader.readEntry();
1798        if (entry == null)
1799        {
1800          break;
1801        }
1802        else
1803        {
1804          entryList.add(entry);
1805        }
1806      }
1807      catch (final Exception e)
1808      {
1809        Debug.debugException(e);
1810        throw new LDAPException(ResultCode.PARAM_ERROR,
1811             ERR_MEM_DS_ADD_ENTRIES_LDIF_PARSE_EXCEPTION.get(
1812                  StaticUtils.getExceptionMessage(e)),
1813             e);
1814      }
1815    }
1816
1817    addEntries(entryList);
1818  }
1819
1820
1821
1822  /**
1823   * Processes a simple bind request with the provided DN and password.  Note
1824   * that the bind processing will verify that the provided credentials are
1825   * valid, but it will not alter the server in any way.
1826   *
1827   * @param  bindDN    The bind DN for the bind operation.
1828   * @param  password  The password for the simple bind operation.
1829   *
1830   * @return  The result of processing the bind operation.
1831   *
1832   * @throws  LDAPException  If the server rejects the bind request, or if a
1833   *                         problem occurs while sending the request or reading
1834   *                         the response.
1835   */
1836  @NotNull()
1837  public BindResult bind(@Nullable final String bindDN,
1838                         @Nullable final String password)
1839         throws LDAPException
1840  {
1841    return bind(new SimpleBindRequest(bindDN, password));
1842  }
1843
1844
1845
1846  /**
1847   * Processes the provided bind request.  Only simple and SASL PLAIN bind
1848   * requests are supported.  Note that the bind processing will verify that the
1849   * provided credentials are valid, but it will not alter the server in any
1850   * way.
1851   *
1852   * @param  bindRequest  The bind request to be processed.  It must not be
1853   *                      {@code null}.
1854   *
1855   * @return  The result of processing the bind operation.
1856   *
1857   * @throws  LDAPException  If the server rejects the bind request, or if a
1858   *                         problem occurs while sending the request or reading
1859   *                         the response.
1860   */
1861  @NotNull()
1862  public BindResult bind(@NotNull final BindRequest bindRequest)
1863         throws LDAPException
1864  {
1865    final ArrayList<Control> requestControlList =
1866         new ArrayList<>(bindRequest.getControlList());
1867    requestControlList.add(new Control(
1868         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1869
1870    final BindRequestProtocolOp bindOp;
1871    if (bindRequest instanceof SimpleBindRequest)
1872    {
1873      final SimpleBindRequest r = (SimpleBindRequest) bindRequest;
1874      bindOp = new BindRequestProtocolOp(r.getBindDN(),
1875           r.getPassword().getValue());
1876    }
1877    else if (bindRequest instanceof PLAINBindRequest)
1878    {
1879      final PLAINBindRequest r = (PLAINBindRequest) bindRequest;
1880
1881      // Create the byte array that should comprise the credentials.
1882      final byte[] authZIDBytes = StaticUtils.getBytes(r.getAuthorizationID());
1883      final byte[] authNIDBytes = StaticUtils.getBytes(r.getAuthenticationID());
1884      final byte[] passwordBytes = r.getPasswordBytes();
1885
1886      final byte[] credBytes = new byte[2 + authZIDBytes.length +
1887           authNIDBytes.length + passwordBytes.length];
1888      System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length);
1889
1890      int pos = authZIDBytes.length + 1;
1891      System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length);
1892
1893      pos += authNIDBytes.length + 1;
1894      System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length);
1895
1896      bindOp = new BindRequestProtocolOp(null, "PLAIN",
1897           new ASN1OctetString(credBytes));
1898    }
1899    else
1900    {
1901      throw new LDAPException(ResultCode.AUTH_METHOD_NOT_SUPPORTED,
1902           ERR_MEM_DS_UNSUPPORTED_BIND_TYPE.get());
1903    }
1904
1905    final LDAPMessage responseMessage = inMemoryHandler.processBindRequest(1,
1906         bindOp, requestControlList);
1907    final BindResponseProtocolOp bindResponse =
1908         responseMessage.getBindResponseProtocolOp();
1909
1910    final BindResult bindResult = new BindResult(new LDAPResult(
1911         responseMessage.getMessageID(),
1912         ResultCode.valueOf(bindResponse.getResultCode()),
1913         bindResponse.getDiagnosticMessage(), bindResponse.getMatchedDN(),
1914         bindResponse.getReferralURLs(), responseMessage.getControls()));
1915
1916    switch (bindResponse.getResultCode())
1917    {
1918      case ResultCode.SUCCESS_INT_VALUE:
1919        return bindResult;
1920      default:
1921        throw new LDAPException(bindResult);
1922    }
1923  }
1924
1925
1926
1927  /**
1928   * {@inheritDoc}
1929   * <BR><BR>
1930   * This method may be used regardless of whether the server is listening for
1931   * client connections, and regardless of whether compare operations are
1932   * allowed in the server.
1933   */
1934  @Override()
1935  @NotNull()
1936  public CompareResult compare(@NotNull final String dn,
1937                               @NotNull final String attributeName,
1938                               @NotNull final String assertionValue)
1939         throws LDAPException
1940  {
1941    return compare(new CompareRequest(dn, attributeName, assertionValue));
1942  }
1943
1944
1945
1946  /**
1947   * {@inheritDoc}
1948   * <BR><BR>
1949   * This method may be used regardless of whether the server is listening for
1950   * client connections, and regardless of whether compare operations are
1951   * allowed in the server.
1952   */
1953  @Override()
1954  @NotNull()
1955  public CompareResult compare(@NotNull final CompareRequest compareRequest)
1956         throws LDAPException
1957  {
1958    final ArrayList<Control> requestControlList =
1959         new ArrayList<>(compareRequest.getControlList());
1960    requestControlList.add(new Control(
1961         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1962
1963    final LDAPMessage responseMessage = inMemoryHandler.processCompareRequest(1,
1964         new CompareRequestProtocolOp(compareRequest.getDN(),
1965              compareRequest.getAttributeName(),
1966              compareRequest.getRawAssertionValue()),
1967         requestControlList);
1968
1969    final CompareResponseProtocolOp compareResponse =
1970         responseMessage.getCompareResponseProtocolOp();
1971
1972    final LDAPResult compareResult = new LDAPResult(
1973         responseMessage.getMessageID(),
1974         ResultCode.valueOf(compareResponse.getResultCode()),
1975         compareResponse.getDiagnosticMessage(), compareResponse.getMatchedDN(),
1976         compareResponse.getReferralURLs(), responseMessage.getControls());
1977
1978    switch (compareResponse.getResultCode())
1979    {
1980      case ResultCode.COMPARE_TRUE_INT_VALUE:
1981      case ResultCode.COMPARE_FALSE_INT_VALUE:
1982        return new CompareResult(compareResult);
1983      default:
1984        throw new LDAPException(compareResult);
1985    }
1986  }
1987
1988
1989
1990  /**
1991   * {@inheritDoc}
1992   * <BR><BR>
1993   * This method may be used regardless of whether the server is listening for
1994   * client connections, and regardless of whether compare operations are
1995   * allowed in the server.
1996   */
1997  @Override()
1998  @NotNull()
1999  public CompareResult compare(
2000              @NotNull final ReadOnlyCompareRequest compareRequest)
2001         throws LDAPException
2002  {
2003    return compare(compareRequest.duplicate());
2004  }
2005
2006
2007
2008  /**
2009   * {@inheritDoc}
2010   * <BR><BR>
2011   * This method may be used regardless of whether the server is listening for
2012   * client connections, and regardless of whether delete operations are
2013   * allowed in the server.
2014   */
2015  @Override()
2016  @NotNull()
2017  public LDAPResult delete(@NotNull final String dn)
2018         throws LDAPException
2019  {
2020    return delete(new DeleteRequest(dn));
2021  }
2022
2023
2024
2025  /**
2026   * {@inheritDoc}
2027   * <BR><BR>
2028   * This method may be used regardless of whether the server is listening for
2029   * client connections, and regardless of whether delete operations are
2030   * allowed in the server.
2031   */
2032  @Override()
2033  @NotNull()
2034  public LDAPResult delete(@NotNull final DeleteRequest deleteRequest)
2035         throws LDAPException
2036  {
2037    return inMemoryHandler.delete(deleteRequest);
2038  }
2039
2040
2041
2042  /**
2043   * {@inheritDoc}
2044   * <BR><BR>
2045   * This method may be used regardless of whether the server is listening for
2046   * client connections, and regardless of whether delete operations are
2047   * allowed in the server.
2048   */
2049  @Override()
2050  @NotNull()
2051  public LDAPResult delete(@NotNull final ReadOnlyDeleteRequest deleteRequest)
2052         throws LDAPException
2053  {
2054    return delete(deleteRequest.duplicate());
2055  }
2056
2057
2058
2059  /**
2060   * Attempts to delete the specified entry and all entries below it from the
2061   * server.
2062   * <BR><BR>
2063   * This method may be used regardless of whether the server is listening for
2064   * client connections, and regardless of whether compare operations are
2065   * allowed in the server.
2066   *
2067   * @param  baseDN  The DN of the entry to remove, along with all of its
2068   *                 subordinates.
2069   *
2070   * @return  The number of entries removed from the server, or zero if the
2071   *          specified entry was not found.
2072   *
2073   * @throws  LDAPException  If a problem is encountered while attempting to
2074   *                         remove the entries.
2075   */
2076  public int deleteSubtree(@NotNull final String baseDN)
2077         throws LDAPException
2078  {
2079    return inMemoryHandler.deleteSubtree(baseDN);
2080  }
2081
2082
2083
2084  /**
2085   * Processes an extended request with the provided request OID.  Note that
2086   * because some types of extended operations return unusual result codes under
2087   * "normal" conditions, the server may not always throw an exception for a
2088   * failed extended operation like it does for other types of operations.  It
2089   * will throw an exception under conditions where there appears to be a
2090   * problem with the connection or the server to which the connection is
2091   * established, but there may be many circumstances in which an extended
2092   * operation is not processed correctly but this method does not throw an
2093   * exception.  In the event that no exception is thrown, it is the
2094   * responsibility of the caller to interpret the result to determine whether
2095   * the operation was processed as expected.
2096   * <BR><BR>
2097   * This method may be used regardless of whether the server is listening for
2098   * client connections, and regardless of whether extended operations are
2099   * allowed in the server.
2100   *
2101   * @param  requestOID  The OID for the extended request to process.  It must
2102   *                     not be {@code null}.
2103   *
2104   * @return  The extended result object that provides information about the
2105   *          result of the request processing.  It may or may not indicate that
2106   *          the operation was successful.
2107   *
2108   * @throws  LDAPException  If a problem occurs while sending the request or
2109   *                         reading the response.
2110   */
2111  @NotNull()
2112  public ExtendedResult processExtendedOperation(
2113                             @NotNull final String requestOID)
2114         throws LDAPException
2115  {
2116    Validator.ensureNotNull(requestOID);
2117
2118    return processExtendedOperation(new ExtendedRequest(requestOID));
2119  }
2120
2121
2122
2123  /**
2124   * Processes an extended request with the provided request OID and value.
2125   * Note that because some types of extended operations return unusual result
2126   * codes under "normal" conditions, the server may not always throw an
2127   * exception for a failed extended operation like it does for other types of
2128   * operations.  It will throw an exception under conditions where there
2129   * appears to be a problem with the connection or the server to which the
2130   * connection is established, but there may be many circumstances in which an
2131   * extended operation is not processed correctly but this method does not
2132   * throw an exception.  In the event that no exception is thrown, it is the
2133   * responsibility of the caller to interpret the result to determine whether
2134   * the operation was processed as expected.
2135   * <BR><BR>
2136   * This method may be used regardless of whether the server is listening for
2137   * client connections, and regardless of whether extended operations are
2138   * allowed in the server.
2139   *
2140   * @param  requestOID    The OID for the extended request to process.  It must
2141   *                       not be {@code null}.
2142   * @param  requestValue  The encoded value for the extended request to
2143   *                       process.  It may be {@code null} if there does not
2144   *                       need to be a value for the requested operation.
2145   *
2146   * @return  The extended result object that provides information about the
2147   *          result of the request processing.  It may or may not indicate that
2148   *          the operation was successful.
2149   *
2150   * @throws  LDAPException  If a problem occurs while sending the request or
2151   *                         reading the response.
2152   */
2153  @NotNull()
2154  public ExtendedResult processExtendedOperation(
2155                             @NotNull final String requestOID,
2156                             @Nullable final ASN1OctetString requestValue)
2157         throws LDAPException
2158  {
2159    Validator.ensureNotNull(requestOID);
2160
2161    return processExtendedOperation(new ExtendedRequest(requestOID,
2162         requestValue));
2163  }
2164
2165
2166
2167  /**
2168   * Processes the provided extended request.  Note that because some types of
2169   * extended operations return unusual result codes under "normal" conditions,
2170   * the server may not always throw an exception for a failed extended
2171   * operation like it does for other types of operations.  It will throw an
2172   * exception under conditions where there appears to be a problem with the
2173   * connection or the server to which the connection is established, but there
2174   * may be many circumstances in which an extended operation is not processed
2175   * correctly but this method does not throw an exception.  In the event that
2176   * no exception is thrown, it is the responsibility of the caller to interpret
2177   * the result to determine whether the operation was processed as expected.
2178   * <BR><BR>
2179   * This method may be used regardless of whether the server is listening for
2180   * client connections, and regardless of whether extended operations are
2181   * allowed in the server.
2182   *
2183   * @param  extendedRequest  The extended request to be processed.  It must not
2184   *                          be {@code null}.
2185   *
2186   * @return  The extended result object that provides information about the
2187   *          result of the request processing.  It may or may not indicate that
2188   *          the operation was successful.
2189   *
2190   * @throws  LDAPException  If a problem occurs while sending the request or
2191   *                         reading the response.
2192   */
2193  @NotNull()
2194  public ExtendedResult processExtendedOperation(
2195                               @NotNull final ExtendedRequest extendedRequest)
2196         throws LDAPException
2197  {
2198    Validator.ensureNotNull(extendedRequest);
2199
2200    final ArrayList<Control> requestControlList =
2201         new ArrayList<>(extendedRequest.getControlList());
2202    requestControlList.add(new Control(
2203         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2204
2205
2206    final LDAPMessage responseMessage =
2207         inMemoryHandler.processExtendedRequest(1,
2208              new ExtendedRequestProtocolOp(extendedRequest.getOID(),
2209                   extendedRequest.getValue()),
2210              requestControlList);
2211
2212    final ExtendedResponseProtocolOp extendedResponse =
2213         responseMessage.getExtendedResponseProtocolOp();
2214
2215    final ResultCode rc = ResultCode.valueOf(extendedResponse.getResultCode());
2216
2217    final String[] referralURLs;
2218    final List<String> referralURLList = extendedResponse.getReferralURLs();
2219    if ((referralURLList == null) || referralURLList.isEmpty())
2220    {
2221      referralURLs = StaticUtils.NO_STRINGS;
2222    }
2223    else
2224    {
2225      referralURLs = new String[referralURLList.size()];
2226      referralURLList.toArray(referralURLs);
2227    }
2228
2229    final Control[] responseControls;
2230    final List<Control> controlList = responseMessage.getControls();
2231    if ((controlList == null) || controlList.isEmpty())
2232    {
2233      responseControls = StaticUtils.NO_CONTROLS;
2234    }
2235    else
2236    {
2237      responseControls = new Control[controlList.size()];
2238      controlList.toArray(responseControls);
2239    }
2240
2241    final ExtendedResult extendedResult = new ExtendedResult(
2242         responseMessage.getMessageID(), rc,
2243         extendedResponse.getDiagnosticMessage(),
2244         extendedResponse.getMatchedDN(), referralURLs,
2245         extendedResponse.getResponseOID(),
2246         extendedResponse.getResponseValue(), responseControls);
2247
2248    if ((extendedResult.getOID() == null) &&
2249        (extendedResult.getValue() == null))
2250    {
2251      switch (rc.intValue())
2252      {
2253        case ResultCode.OPERATIONS_ERROR_INT_VALUE:
2254        case ResultCode.PROTOCOL_ERROR_INT_VALUE:
2255        case ResultCode.BUSY_INT_VALUE:
2256        case ResultCode.UNAVAILABLE_INT_VALUE:
2257        case ResultCode.OTHER_INT_VALUE:
2258        case ResultCode.SERVER_DOWN_INT_VALUE:
2259        case ResultCode.LOCAL_ERROR_INT_VALUE:
2260        case ResultCode.ENCODING_ERROR_INT_VALUE:
2261        case ResultCode.DECODING_ERROR_INT_VALUE:
2262        case ResultCode.TIMEOUT_INT_VALUE:
2263        case ResultCode.NO_MEMORY_INT_VALUE:
2264        case ResultCode.CONNECT_ERROR_INT_VALUE:
2265          throw new LDAPException(extendedResult);
2266      }
2267    }
2268
2269    return extendedResult;
2270  }
2271
2272
2273
2274  /**
2275   * {@inheritDoc}
2276   * <BR><BR>
2277   * This method may be used regardless of whether the server is listening for
2278   * client connections, and regardless of whether modify operations are allowed
2279   * in the server.
2280   */
2281  @Override()
2282  @NotNull()
2283  public LDAPResult modify(@NotNull final String dn,
2284                           @NotNull final Modification mod)
2285         throws LDAPException
2286  {
2287    return modify(new ModifyRequest(dn, mod));
2288  }
2289
2290
2291
2292  /**
2293   * {@inheritDoc}
2294   * <BR><BR>
2295   * This method may be used regardless of whether the server is listening for
2296   * client connections, and regardless of whether modify operations are allowed
2297   * in the server.
2298   */
2299  @Override()
2300  @NotNull()
2301  public LDAPResult modify(@NotNull final String dn,
2302                           @NotNull final Modification... mods)
2303         throws LDAPException
2304  {
2305    return modify(new ModifyRequest(dn, mods));
2306  }
2307
2308
2309
2310  /**
2311   * {@inheritDoc}
2312   * <BR><BR>
2313   * This method may be used regardless of whether the server is listening for
2314   * client connections, and regardless of whether modify operations are allowed
2315   * in the server.
2316   */
2317  @Override()
2318  @NotNull()
2319  public LDAPResult modify(@NotNull final String dn,
2320                           @NotNull final List<Modification> mods)
2321         throws LDAPException
2322  {
2323    return modify(new ModifyRequest(dn, mods));
2324  }
2325
2326
2327
2328  /**
2329   * {@inheritDoc}
2330   * <BR><BR>
2331   * This method may be used regardless of whether the server is listening for
2332   * client connections, and regardless of whether modify operations are allowed
2333   * in the server.
2334   */
2335  @Override()
2336  @NotNull()
2337  public LDAPResult modify(@NotNull final String... ldifModificationLines)
2338         throws LDIFException, LDAPException
2339  {
2340    return modify(new ModifyRequest(ldifModificationLines));
2341  }
2342
2343
2344
2345  /**
2346   * {@inheritDoc}
2347   * <BR><BR>
2348   * This method may be used regardless of whether the server is listening for
2349   * client connections, and regardless of whether modify operations are allowed
2350   * in the server.
2351   */
2352  @Override()
2353  @NotNull()
2354  public LDAPResult modify(@NotNull final ModifyRequest modifyRequest)
2355         throws LDAPException
2356  {
2357    return inMemoryHandler.modify(modifyRequest);
2358  }
2359
2360
2361
2362  /**
2363   * {@inheritDoc}
2364   * <BR><BR>
2365   * This method may be used regardless of whether the server is listening for
2366   * client connections, and regardless of whether modify operations are allowed
2367   * in the server.
2368   */
2369  @Override()
2370  @NotNull()
2371  public LDAPResult modify(@NotNull final ReadOnlyModifyRequest modifyRequest)
2372         throws LDAPException
2373  {
2374    return modify(modifyRequest.duplicate());
2375  }
2376
2377
2378
2379  /**
2380   * {@inheritDoc}
2381   * <BR><BR>
2382   * This method may be used regardless of whether the server is listening for
2383   * client connections, and regardless of whether modify DN operations are
2384   * allowed in the server.
2385   */
2386  @Override()
2387  @NotNull()
2388  public LDAPResult modifyDN(@NotNull final String dn,
2389                             @NotNull final String newRDN,
2390                             final boolean deleteOldRDN)
2391         throws LDAPException
2392  {
2393    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
2394  }
2395
2396
2397
2398  /**
2399   * {@inheritDoc}
2400   * <BR><BR>
2401   * This method may be used regardless of whether the server is listening for
2402   * client connections, and regardless of whether modify DN operations are
2403   * allowed in the server.
2404   */
2405  @Override()
2406  @NotNull()
2407  public LDAPResult modifyDN(@NotNull final String dn,
2408                             @NotNull final String newRDN,
2409                             final boolean deleteOldRDN,
2410                             @Nullable final String newSuperiorDN)
2411         throws LDAPException
2412  {
2413    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
2414         newSuperiorDN));
2415  }
2416
2417
2418
2419  /**
2420   * {@inheritDoc}
2421   * <BR><BR>
2422   * This method may be used regardless of whether the server is listening for
2423   * client connections, and regardless of whether modify DN operations are
2424   * allowed in the server.
2425   */
2426  @Override()
2427  @NotNull()
2428  public LDAPResult modifyDN(@NotNull final ModifyDNRequest modifyDNRequest)
2429         throws LDAPException
2430  {
2431    return inMemoryHandler.modifyDN(modifyDNRequest);
2432  }
2433
2434
2435
2436  /**
2437   * {@inheritDoc}
2438   * <BR><BR>
2439   * This method may be used regardless of whether the server is listening for
2440   * client connections, and regardless of whether modify DN operations are
2441   * allowed in the server.
2442   */
2443  @Override()
2444  @NotNull()
2445  public LDAPResult modifyDN(
2446              @NotNull final ReadOnlyModifyDNRequest modifyDNRequest)
2447         throws LDAPException
2448  {
2449    return modifyDN(modifyDNRequest.duplicate());
2450  }
2451
2452
2453
2454  /**
2455   * {@inheritDoc}
2456   * <BR><BR>
2457   * This method may be used regardless of whether the server is listening for
2458   * client connections, and regardless of whether search operations are allowed
2459   * in the server.
2460   */
2461  @Override()
2462  @NotNull()
2463  public SearchResult search(@NotNull final String baseDN,
2464                             @NotNull final SearchScope scope,
2465                             @NotNull final String filter,
2466                             @Nullable final String... attributes)
2467         throws LDAPSearchException
2468  {
2469    return search(new SearchRequest(baseDN, scope, parseFilter(filter),
2470         attributes));
2471  }
2472
2473
2474
2475  /**
2476   * {@inheritDoc}
2477   * <BR><BR>
2478   * This method may be used regardless of whether the server is listening for
2479   * client connections, and regardless of whether search operations are allowed
2480   * in the server.
2481   */
2482  @Override()
2483  @NotNull()
2484  public SearchResult search(@NotNull final String baseDN,
2485                             @NotNull final SearchScope scope,
2486                             @NotNull final Filter filter,
2487                             @Nullable final String... attributes)
2488         throws LDAPSearchException
2489  {
2490    return search(new SearchRequest(baseDN, scope, filter, attributes));
2491  }
2492
2493
2494
2495  /**
2496   * {@inheritDoc}
2497   * <BR><BR>
2498   * This method may be used regardless of whether the server is listening for
2499   * client connections, and regardless of whether search operations are allowed
2500   * in the server.
2501   */
2502  @Override()
2503  @NotNull()
2504  public SearchResult search(
2505               @Nullable final SearchResultListener searchResultListener,
2506               @NotNull final String baseDN, @NotNull final SearchScope scope,
2507               @NotNull final String filter,
2508               @Nullable final String... attributes)
2509         throws LDAPSearchException
2510  {
2511    return search(new SearchRequest(searchResultListener, baseDN, scope,
2512         parseFilter(filter), attributes));
2513  }
2514
2515
2516
2517  /**
2518   * {@inheritDoc}
2519   * <BR><BR>
2520   * This method may be used regardless of whether the server is listening for
2521   * client connections, and regardless of whether search operations are allowed
2522   * in the server.
2523   */
2524  @Override()
2525  @NotNull()
2526  public SearchResult search(
2527              @Nullable final SearchResultListener searchResultListener,
2528              @NotNull final String baseDN, @NotNull final SearchScope scope,
2529              @NotNull final Filter filter,
2530              @Nullable final String... attributes)
2531         throws LDAPSearchException
2532  {
2533    return search(new SearchRequest(searchResultListener, baseDN, scope,
2534         filter, attributes));
2535  }
2536
2537
2538
2539  /**
2540   * {@inheritDoc}
2541   * <BR><BR>
2542   * This method may be used regardless of whether the server is listening for
2543   * client connections, and regardless of whether search operations are allowed
2544   * in the server.
2545   */
2546  @Override()
2547  @NotNull()
2548  public SearchResult search(@NotNull final String baseDN,
2549                             @NotNull final SearchScope scope,
2550                             @NotNull final DereferencePolicy derefPolicy,
2551                             final int sizeLimit, final int timeLimit,
2552                             final boolean typesOnly,
2553                             @NotNull final String filter,
2554                             @Nullable final String... attributes)
2555         throws LDAPSearchException
2556  {
2557    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2558         timeLimit, typesOnly, parseFilter(filter), attributes));
2559  }
2560
2561
2562
2563  /**
2564   * {@inheritDoc}
2565   * <BR><BR>
2566   * This method may be used regardless of whether the server is listening for
2567   * client connections, and regardless of whether search operations are allowed
2568   * in the server.
2569   */
2570  @Override()
2571  @NotNull()
2572  public SearchResult search(@NotNull final String baseDN,
2573                             @NotNull final SearchScope scope,
2574                             @NotNull final DereferencePolicy derefPolicy,
2575                             final int sizeLimit, final int timeLimit,
2576                             final boolean typesOnly,
2577                             @NotNull final Filter filter,
2578                             @Nullable final String... attributes)
2579         throws LDAPSearchException
2580  {
2581    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2582         timeLimit, typesOnly, filter, attributes));
2583  }
2584
2585
2586
2587  /**
2588   * {@inheritDoc}
2589   * <BR><BR>
2590   * This method may be used regardless of whether the server is listening for
2591   * client connections, and regardless of whether search operations are allowed
2592   * in the server.
2593   */
2594  @Override()
2595  @NotNull()
2596  public SearchResult search(
2597              @Nullable final SearchResultListener searchResultListener,
2598              @NotNull final String baseDN, @NotNull final SearchScope scope,
2599              @NotNull final DereferencePolicy derefPolicy, final int sizeLimit,
2600              final int timeLimit, final boolean typesOnly,
2601              @NotNull final String filter,
2602              @Nullable final String... attributes)
2603         throws LDAPSearchException
2604  {
2605    return search(new SearchRequest(searchResultListener, baseDN, scope,
2606         derefPolicy, sizeLimit, timeLimit, typesOnly, parseFilter(filter),
2607         attributes));
2608  }
2609
2610
2611
2612  /**
2613   * {@inheritDoc}
2614   * <BR><BR>
2615   * This method may be used regardless of whether the server is listening for
2616   * client connections, and regardless of whether search operations are allowed
2617   * in the server.
2618   */
2619  @Override()
2620  @NotNull()
2621  public SearchResult search(
2622              @Nullable final SearchResultListener searchResultListener,
2623              @NotNull final String baseDN, @NotNull final SearchScope scope,
2624              @NotNull final DereferencePolicy derefPolicy, final int sizeLimit,
2625              final int timeLimit, final boolean typesOnly,
2626              @NotNull final Filter filter,
2627              @Nullable final String... attributes)
2628         throws LDAPSearchException
2629  {
2630    return search(new SearchRequest(searchResultListener, baseDN, scope,
2631         derefPolicy, sizeLimit, timeLimit, typesOnly, filter, attributes));
2632  }
2633
2634
2635
2636  /**
2637   * {@inheritDoc}
2638   * <BR><BR>
2639   * This method may be used regardless of whether the server is listening for
2640   * client connections, and regardless of whether search operations are allowed
2641   * in the server.
2642   */
2643  @Override()
2644  @NotNull()
2645  public SearchResult search(@NotNull final SearchRequest searchRequest)
2646         throws LDAPSearchException
2647  {
2648    final ArrayList<Control> requestControlList =
2649         new ArrayList<>(searchRequest.getControlList());
2650    requestControlList.add(new Control(
2651         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2652
2653    final List<SearchResultEntry> entryList =
2654         new ArrayList<>(10);
2655    final List<SearchResultReference> referenceList =
2656         new ArrayList<>(10);
2657
2658    final LDAPMessage responseMessage = inMemoryHandler.processSearchRequest(1,
2659         new SearchRequestProtocolOp(searchRequest.getBaseDN(),
2660              searchRequest.getScope(), searchRequest.getDereferencePolicy(),
2661              searchRequest.getSizeLimit(), searchRequest.getTimeLimitSeconds(),
2662              searchRequest.typesOnly(), searchRequest.getFilter(),
2663              searchRequest.getAttributeList()),
2664         requestControlList, entryList, referenceList);
2665
2666
2667    final List<SearchResultEntry> returnEntryList;
2668    final List<SearchResultReference> returnReferenceList;
2669    final SearchResultListener searchListener =
2670         searchRequest.getSearchResultListener();
2671    if (searchListener == null)
2672    {
2673      returnEntryList = Collections.unmodifiableList(entryList);
2674      returnReferenceList = Collections.unmodifiableList(referenceList);
2675    }
2676    else
2677    {
2678      returnEntryList     = null;
2679      returnReferenceList = null;
2680
2681      for (final SearchResultEntry e : entryList)
2682      {
2683        searchListener.searchEntryReturned(e);
2684      }
2685
2686      for (final SearchResultReference r : referenceList)
2687      {
2688        searchListener.searchReferenceReturned(r);
2689      }
2690    }
2691
2692
2693    final SearchResultDoneProtocolOp searchDone =
2694         responseMessage.getSearchResultDoneProtocolOp();
2695
2696    final ResultCode rc = ResultCode.valueOf(searchDone.getResultCode());
2697
2698    final String[] referralURLs;
2699    final List<String> referralURLList = searchDone.getReferralURLs();
2700    if ((referralURLList == null) || referralURLList.isEmpty())
2701    {
2702      referralURLs = StaticUtils.NO_STRINGS;
2703    }
2704    else
2705    {
2706      referralURLs = new String[referralURLList.size()];
2707      referralURLList.toArray(referralURLs);
2708    }
2709
2710    final Control[] responseControls;
2711    final List<Control> controlList = responseMessage.getControls();
2712    if ((controlList == null) || controlList.isEmpty())
2713    {
2714      responseControls = StaticUtils.NO_CONTROLS;
2715    }
2716    else
2717    {
2718      responseControls = new Control[controlList.size()];
2719      controlList.toArray(responseControls);
2720    }
2721
2722    final SearchResult searchResult =new SearchResult(
2723         responseMessage.getMessageID(), rc, searchDone.getDiagnosticMessage(),
2724         searchDone.getMatchedDN(), referralURLs, returnEntryList,
2725         returnReferenceList, entryList.size(), referenceList.size(),
2726         responseControls);
2727
2728    if (rc == ResultCode.SUCCESS)
2729    {
2730      return searchResult;
2731    }
2732    else
2733    {
2734      throw new LDAPSearchException(searchResult);
2735    }
2736  }
2737
2738
2739
2740  /**
2741   * {@inheritDoc}
2742   * <BR><BR>
2743   * This method may be used regardless of whether the server is listening for
2744   * client connections, and regardless of whether search operations are allowed
2745   * in the server.
2746   */
2747  @Override()
2748  @NotNull()
2749  public SearchResult search(@NotNull final ReadOnlySearchRequest searchRequest)
2750         throws LDAPSearchException
2751  {
2752    return search(searchRequest.duplicate());
2753  }
2754
2755
2756
2757  /**
2758   * {@inheritDoc}
2759   * <BR><BR>
2760   * This method may be used regardless of whether the server is listening for
2761   * client connections, and regardless of whether search operations are allowed
2762   * in the server.
2763   */
2764  @Override()
2765  @Nullable()
2766  public SearchResultEntry searchForEntry(@NotNull final String baseDN,
2767                                          @NotNull final SearchScope scope,
2768                                          @NotNull final String filter,
2769                                          @Nullable final String... attributes)
2770         throws LDAPSearchException
2771  {
2772    return searchForEntry(new SearchRequest(baseDN, scope, parseFilter(filter),
2773         attributes));
2774  }
2775
2776
2777
2778  /**
2779   * {@inheritDoc}
2780   * <BR><BR>
2781   * This method may be used regardless of whether the server is listening for
2782   * client connections, and regardless of whether search operations are allowed
2783   * in the server.
2784   */
2785  @Override()
2786  @Nullable()
2787  public SearchResultEntry searchForEntry(@NotNull final String baseDN,
2788                                          @NotNull final SearchScope scope,
2789                                          @NotNull final Filter filter,
2790                                          @Nullable final String... attributes)
2791         throws LDAPSearchException
2792  {
2793    return searchForEntry(new SearchRequest(baseDN, scope, filter, attributes));
2794  }
2795
2796
2797
2798  /**
2799   * {@inheritDoc}
2800   * <BR><BR>
2801   * This method may be used regardless of whether the server is listening for
2802   * client connections, and regardless of whether search operations are allowed
2803   * in the server.
2804   */
2805  @Override()
2806  @Nullable()
2807  public SearchResultEntry searchForEntry(@NotNull final String baseDN,
2808              @NotNull final SearchScope scope,
2809              @NotNull final DereferencePolicy derefPolicy,
2810              final int timeLimit, final boolean typesOnly,
2811              @NotNull final String filter,
2812              @Nullable final String... attributes)
2813         throws LDAPSearchException
2814  {
2815    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2816         timeLimit, typesOnly, parseFilter(filter), attributes));
2817  }
2818
2819
2820
2821  /**
2822   * {@inheritDoc}
2823   * <BR><BR>
2824   * This method may be used regardless of whether the server is listening for
2825   * client connections, and regardless of whether search operations are allowed
2826   * in the server.
2827   */
2828  @Override()
2829  @Nullable()
2830  public SearchResultEntry searchForEntry(@NotNull final String baseDN,
2831              @NotNull final SearchScope scope,
2832              @NotNull final DereferencePolicy derefPolicy,
2833              final int timeLimit, final boolean typesOnly,
2834              @NotNull final Filter filter,
2835              @Nullable final String... attributes)
2836         throws LDAPSearchException
2837  {
2838    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2839         timeLimit, typesOnly, filter, attributes));
2840  }
2841
2842
2843
2844  /**
2845   * {@inheritDoc}
2846   * <BR><BR>
2847   * This method may be used regardless of whether the server is listening for
2848   * client connections, and regardless of whether search operations are allowed
2849   * in the server.
2850   */
2851  @Override()
2852  @Nullable()
2853  public SearchResultEntry searchForEntry(
2854                                @NotNull final SearchRequest searchRequest)
2855         throws LDAPSearchException
2856  {
2857    final ArrayList<Control> requestControlList =
2858         new ArrayList<>(searchRequest.getControlList());
2859    requestControlList.add(new Control(
2860         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2861
2862    final SearchRequest r;
2863    if ((searchRequest.getSizeLimit() == 1) &&
2864        (searchRequest.getSearchResultListener() == null))
2865    {
2866      r = searchRequest;
2867    }
2868    else
2869    {
2870      r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(),
2871           searchRequest.getDereferencePolicy(), 1,
2872           searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(),
2873           searchRequest.getFilter(), searchRequest.getAttributes());
2874
2875      r.setFollowReferrals(InternalSDKHelper.followReferralsInternal(r));
2876      r.setReferralConnector(InternalSDKHelper.getReferralConnectorInternal(r));
2877      r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null));
2878      r.setControls(requestControlList);
2879    }
2880
2881    final SearchResult result;
2882    try
2883    {
2884      result = search(r);
2885    }
2886    catch (final LDAPSearchException lse)
2887    {
2888      Debug.debugException(lse);
2889
2890      if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT)
2891      {
2892        return null;
2893      }
2894
2895      throw lse;
2896    }
2897
2898    if (result.getEntryCount() == 0)
2899    {
2900      return null;
2901    }
2902    else
2903    {
2904      return result.getSearchEntries().get(0);
2905    }
2906  }
2907
2908
2909
2910  /**
2911   * {@inheritDoc}
2912   * <BR><BR>
2913   * This method may be used regardless of whether the server is listening for
2914   * client connections, and regardless of whether search operations are allowed
2915   * in the server.
2916   */
2917  @Override()
2918  @Nullable()
2919  public SearchResultEntry searchForEntry(
2920              @NotNull final ReadOnlySearchRequest searchRequest)
2921         throws LDAPSearchException
2922  {
2923    return searchForEntry(searchRequest.duplicate());
2924  }
2925
2926
2927
2928  /**
2929   * Retrieves the configured list of password attributes.
2930   *
2931   * @return  The configured list of password attributes.
2932   */
2933  @NotNull()
2934  public List<String> getPasswordAttributes()
2935  {
2936    return inMemoryHandler.getPasswordAttributes();
2937  }
2938
2939
2940
2941  /**
2942   * Retrieves the primary password encoder that has been configured for the
2943   * server.
2944   *
2945   * @return  The primary password encoder that has been configured for the
2946   *          server.
2947   */
2948  @Nullable()
2949  public InMemoryPasswordEncoder getPrimaryPasswordEncoder()
2950  {
2951    return inMemoryHandler.getPrimaryPasswordEncoder();
2952  }
2953
2954
2955
2956  /**
2957   * Retrieves a list of all password encoders configured for the server.
2958   *
2959   * @return  A list of all password encoders configured for the server.
2960   */
2961  @NotNull()
2962  public List<InMemoryPasswordEncoder> getAllPasswordEncoders()
2963  {
2964    return inMemoryHandler.getAllPasswordEncoders();
2965  }
2966
2967
2968
2969  /**
2970   * Retrieves a list of the passwords contained in the provided entry.
2971   *
2972   * @param  entry                 The entry from which to obtain the list of
2973   *                               passwords.  It must not be {@code null}.
2974   * @param  clearPasswordToMatch  An optional clear-text password that should
2975   *                               match the values that are returned.  If this
2976   *                               is {@code null}, then all passwords contained
2977   *                               in the provided entry will be returned.  If
2978   *                               this is non-{@code null}, then only passwords
2979   *                               matching the clear-text password will be
2980   *                               returned.
2981   *
2982   * @return  A list of the passwords contained in the provided entry,
2983   *          optionally restricted to those matching the provided clear-text
2984   *          password, or an empty list if the entry does not contain any
2985   *          passwords.
2986   */
2987  @NotNull()
2988  public List<InMemoryDirectoryServerPassword> getPasswordsInEntry(
2989              @NotNull final Entry entry,
2990              @Nullable final ASN1OctetString clearPasswordToMatch)
2991  {
2992    return inMemoryHandler.getPasswordsInEntry(entry, clearPasswordToMatch);
2993  }
2994
2995
2996
2997  /**
2998   * Parses the provided string as a search filter.
2999   *
3000   * @param  s  The string to be parsed.
3001   *
3002   * @return  The parsed filter.
3003   *
3004   * @throws  LDAPSearchException  If the provided string could not be parsed as
3005   *                               a valid search filter.
3006   */
3007  @NotNull()
3008  private static Filter parseFilter(@NotNull final String s)
3009          throws LDAPSearchException
3010  {
3011    try
3012    {
3013      return Filter.create(s);
3014    }
3015    catch (final LDAPException le)
3016    {
3017      throw new LDAPSearchException(le);
3018    }
3019  }
3020
3021
3022
3023  /**
3024   * Indicates whether the specified entry exists in the server.
3025   * <BR><BR>
3026   * This method may be used regardless of whether the server is listening for
3027   * client connections.
3028   *
3029   * @param  dn  The DN of the entry for which to make the determination.
3030   *
3031   * @return  {@code true} if the entry exists, or {@code false} if not.
3032   *
3033   * @throws  LDAPException  If a problem is encountered while trying to
3034   *                         communicate with the directory server.
3035   */
3036  public boolean entryExists(@NotNull final String dn)
3037         throws LDAPException
3038  {
3039    return inMemoryHandler.entryExists(dn);
3040  }
3041
3042
3043
3044  /**
3045   * Indicates whether the specified entry exists in the server and matches the
3046   * given filter.
3047   * <BR><BR>
3048   * This method may be used regardless of whether the server is listening for
3049   * client connections.
3050   *
3051   * @param  dn      The DN of the entry for which to make the determination.
3052   * @param  filter  The filter the entry is expected to match.
3053   *
3054   * @return  {@code true} if the entry exists and matches the specified filter,
3055   *          or {@code false} if not.
3056   *
3057   * @throws  LDAPException  If a problem is encountered while trying to
3058   *                         communicate with the directory server.
3059   */
3060  public boolean entryExists(@NotNull final String dn,
3061                             @NotNull final String filter)
3062         throws LDAPException
3063  {
3064    return inMemoryHandler.entryExists(dn, filter);
3065  }
3066
3067
3068
3069  /**
3070   * Indicates whether the specified entry exists in the server.  This will
3071   * return {@code true} only if the target entry exists and contains all values
3072   * for all attributes of the provided entry.  The entry will be allowed to
3073   * have attribute values not included in the provided entry.
3074   * <BR><BR>
3075   * This method may be used regardless of whether the server is listening for
3076   * client connections.
3077   *
3078   * @param  entry  The entry to compare against the directory server.
3079   *
3080   * @return  {@code true} if the entry exists in the server and is a superset
3081   *          of the provided entry, or {@code false} if not.
3082   *
3083   * @throws  LDAPException  If a problem is encountered while trying to
3084   *                         communicate with the directory server.
3085   */
3086  public boolean entryExists(@NotNull final Entry entry)
3087         throws LDAPException
3088  {
3089    return inMemoryHandler.entryExists(entry);
3090  }
3091
3092
3093
3094  /**
3095   * Ensures that an entry with the provided DN exists in the directory.
3096   * <BR><BR>
3097   * This method may be used regardless of whether the server is listening for
3098   * client connections.
3099   *
3100   * @param  dn  The DN of the entry for which to make the determination.
3101   *
3102   * @throws  LDAPException  If a problem is encountered while trying to
3103   *                         communicate with the directory server.
3104   *
3105   * @throws  AssertionError  If the target entry does not exist.
3106   */
3107  public void assertEntryExists(@NotNull final String dn)
3108         throws LDAPException, AssertionError
3109  {
3110    inMemoryHandler.assertEntryExists(dn);
3111  }
3112
3113
3114
3115  /**
3116   * Ensures that an entry with the provided DN exists in the directory.
3117   * <BR><BR>
3118   * This method may be used regardless of whether the server is listening for
3119   * client connections.
3120   *
3121   * @param  dn      The DN of the entry for which to make the determination.
3122   * @param  filter  A filter that the target entry must match.
3123   *
3124   * @throws  LDAPException  If a problem is encountered while trying to
3125   *                         communicate with the directory server.
3126   *
3127   * @throws  AssertionError  If the target entry does not exist or does not
3128   *                          match the provided filter.
3129   */
3130  public void assertEntryExists(@NotNull final String dn,
3131                                @NotNull final String filter)
3132         throws LDAPException, AssertionError
3133  {
3134    inMemoryHandler.assertEntryExists(dn, filter);
3135  }
3136
3137
3138
3139  /**
3140   * Ensures that an entry exists in the directory with the same DN and all
3141   * attribute values contained in the provided entry.  The server entry may
3142   * contain additional attributes and/or attribute values not included in the
3143   * provided entry.
3144   * <BR><BR>
3145   * This method may be used regardless of whether the server is listening for
3146   * client connections.
3147   *
3148   * @param  entry  The entry expected to be present in the directory server.
3149   *
3150   * @throws  LDAPException  If a problem is encountered while trying to
3151   *                         communicate with the directory server.
3152   *
3153   * @throws  AssertionError  If the target entry does not exist or does not
3154   *                          match the provided filter.
3155   */
3156  public void assertEntryExists(@NotNull final Entry entry)
3157         throws LDAPException, AssertionError
3158  {
3159    inMemoryHandler.assertEntryExists(entry);
3160  }
3161
3162
3163
3164  /**
3165   * Retrieves a list containing the DNs of the entries which are missing from
3166   * the directory server.
3167   * <BR><BR>
3168   * This method may be used regardless of whether the server is listening for
3169   * client connections.
3170   *
3171   * @param  dns  The DNs of the entries to try to find in the server.
3172   *
3173   * @return  A list containing all of the provided DNs that were not found in
3174   *          the server, or an empty list if all entries were found.
3175   *
3176   * @throws  LDAPException  If a problem is encountered while trying to
3177   *                         communicate with the directory server.
3178   */
3179  @NotNull()
3180  public List<String> getMissingEntryDNs(@NotNull final String... dns)
3181         throws LDAPException
3182  {
3183    return inMemoryHandler.getMissingEntryDNs(StaticUtils.toList(dns));
3184  }
3185
3186
3187
3188  /**
3189   * Retrieves a list containing the DNs of the entries which are missing from
3190   * the directory server.
3191   * <BR><BR>
3192   * This method may be used regardless of whether the server is listening for
3193   * client connections.
3194   *
3195   * @param  dns  The DNs of the entries to try to find in the server.
3196   *
3197   * @return  A list containing all of the provided DNs that were not found in
3198   *          the server, or an empty list if all entries were found.
3199   *
3200   * @throws  LDAPException  If a problem is encountered while trying to
3201   *                         communicate with the directory server.
3202   */
3203  @NotNull()
3204  public List<String> getMissingEntryDNs(@NotNull final Collection<String> dns)
3205         throws LDAPException
3206  {
3207    return inMemoryHandler.getMissingEntryDNs(dns);
3208  }
3209
3210
3211
3212  /**
3213   * Ensures that all of the entries with the provided DNs exist in the
3214   * directory.
3215   * <BR><BR>
3216   * This method may be used regardless of whether the server is listening for
3217   * client connections.
3218   *
3219   * @param  dns  The DNs of the entries for which to make the determination.
3220   *
3221   * @throws  LDAPException  If a problem is encountered while trying to
3222   *                         communicate with the directory server.
3223   *
3224   * @throws  AssertionError  If any of the target entries does not exist.
3225   */
3226  public void assertEntriesExist(@NotNull final String... dns)
3227         throws LDAPException, AssertionError
3228  {
3229    inMemoryHandler.assertEntriesExist(StaticUtils.toList(dns));
3230  }
3231
3232
3233
3234  /**
3235   * Ensures that all of the entries with the provided DNs exist in the
3236   * directory.
3237   * <BR><BR>
3238   * This method may be used regardless of whether the server is listening for
3239   * client connections.
3240   *
3241   * @param  dns  The DNs of the entries for which to make the determination.
3242   *
3243   * @throws  LDAPException  If a problem is encountered while trying to
3244   *                         communicate with the directory server.
3245   *
3246   * @throws  AssertionError  If any of the target entries does not exist.
3247   */
3248  public void assertEntriesExist(@NotNull final Collection<String> dns)
3249         throws LDAPException, AssertionError
3250  {
3251    inMemoryHandler.assertEntriesExist(dns);
3252  }
3253
3254
3255
3256  /**
3257   * Retrieves a list containing all of the named attributes which do not exist
3258   * in the target entry.
3259   * <BR><BR>
3260   * This method may be used regardless of whether the server is listening for
3261   * client connections.
3262   *
3263   * @param  dn              The DN of the entry to examine.
3264   * @param  attributeNames  The names of the attributes expected to be present
3265   *                         in the target entry.
3266   *
3267   * @return  A list containing the names of the attributes which were not
3268   *          present in the target entry, an empty list if all specified
3269   *          attributes were found in the entry, or {@code null} if the target
3270   *          entry does not exist.
3271   *
3272   * @throws  LDAPException  If a problem is encountered while trying to
3273   *                         communicate with the directory server.
3274   */
3275  @Nullable()
3276  public List<String> getMissingAttributeNames(@NotNull final String dn,
3277                           @NotNull final String... attributeNames)
3278         throws LDAPException
3279  {
3280    return inMemoryHandler.getMissingAttributeNames(dn,
3281         StaticUtils.toList(attributeNames));
3282  }
3283
3284
3285
3286  /**
3287   * Retrieves a list containing all of the named attributes which do not exist
3288   * in the target entry.
3289   * <BR><BR>
3290   * This method may be used regardless of whether the server is listening for
3291   * client connections.
3292   *
3293   * @param  dn              The DN of the entry to examine.
3294   * @param  attributeNames  The names of the attributes expected to be present
3295   *                         in the target entry.
3296   *
3297   * @return  A list containing the names of the attributes which were not
3298   *          present in the target entry, an empty list if all specified
3299   *          attributes were found in the entry, or {@code null} if the target
3300   *          entry does not exist.
3301   *
3302   * @throws  LDAPException  If a problem is encountered while trying to
3303   *                         communicate with the directory server.
3304   */
3305  @Nullable()
3306  public List<String> getMissingAttributeNames(@NotNull final String dn,
3307                           @NotNull final Collection<String> attributeNames)
3308         throws LDAPException
3309  {
3310    return inMemoryHandler.getMissingAttributeNames(dn, attributeNames);
3311  }
3312
3313
3314
3315  /**
3316   * Ensures that the specified entry exists in the directory with all of the
3317   * specified attributes.
3318   * <BR><BR>
3319   * This method may be used regardless of whether the server is listening for
3320   * client connections.
3321   *
3322   * @param  dn              The DN of the entry to examine.
3323   * @param  attributeNames  The names of the attributes that are expected to be
3324   *                         present in the provided entry.
3325   *
3326   * @throws  LDAPException  If a problem is encountered while trying to
3327   *                         communicate with the directory server.
3328   *
3329   * @throws  AssertionError  If the target entry does not exist or does not
3330   *                          contain all of the specified attributes.
3331   */
3332  public void assertAttributeExists(@NotNull final String dn,
3333                                    @NotNull final String... attributeNames)
3334        throws LDAPException, AssertionError
3335  {
3336    inMemoryHandler.assertAttributeExists(dn,
3337         StaticUtils.toList(attributeNames));
3338  }
3339
3340
3341
3342  /**
3343   * Ensures that the specified entry exists in the directory with all of the
3344   * specified attributes.
3345   * <BR><BR>
3346   * This method may be used regardless of whether the server is listening for
3347   * client connections.
3348   *
3349   * @param  dn              The DN of the entry to examine.
3350   * @param  attributeNames  The names of the attributes that are expected to be
3351   *                         present in the provided entry.
3352   *
3353   * @throws  LDAPException  If a problem is encountered while trying to
3354   *                         communicate with the directory server.
3355   *
3356   * @throws  AssertionError  If the target entry does not exist or does not
3357   *                          contain all of the specified attributes.
3358   */
3359  public void assertAttributeExists(@NotNull final String dn,
3360                   @NotNull final Collection<String> attributeNames)
3361        throws LDAPException, AssertionError
3362  {
3363    inMemoryHandler.assertAttributeExists(dn, attributeNames);
3364  }
3365
3366
3367
3368  /**
3369   * Retrieves a list of all provided attribute values which are missing from
3370   * the specified entry.
3371   * <BR><BR>
3372   * This method may be used regardless of whether the server is listening for
3373   * client connections.
3374   *
3375   * @param  dn               The DN of the entry to examine.
3376   * @param  attributeName    The attribute expected to be present in the target
3377   *                          entry with the given values.
3378   * @param  attributeValues  The values expected to be present in the target
3379   *                          entry.
3380   *
3381   * @return  A list containing all of the provided values which were not found
3382   *          in the entry, an empty list if all provided attribute values were
3383   *          found, or {@code null} if the target entry does not exist.
3384   *
3385   * @throws  LDAPException  If a problem is encountered while trying to
3386   *                         communicate with the directory server.
3387   */
3388  @Nullable()
3389  public List<String> getMissingAttributeValues(@NotNull final String dn,
3390                           @NotNull final String attributeName,
3391                           @NotNull final String... attributeValues)
3392         throws LDAPException
3393  {
3394    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3395         StaticUtils.toList(attributeValues));
3396  }
3397
3398
3399
3400  /**
3401   * Retrieves a list of all provided attribute values which are missing from
3402   * the specified entry.  The target attribute may or may not contain
3403   * additional values.
3404   * <BR><BR>
3405   * This method may be used regardless of whether the server is listening for
3406   * client connections.
3407   *
3408   * @param  dn               The DN of the entry to examine.
3409   * @param  attributeName    The attribute expected to be present in the target
3410   *                          entry with the given values.
3411   * @param  attributeValues  The values expected to be present in the target
3412   *                          entry.
3413   *
3414   * @return  A list containing all of the provided values which were not found
3415   *          in the entry, an empty list if all provided attribute values were
3416   *          found, or {@code null} if the target entry does not exist.
3417   *
3418   * @throws  LDAPException  If a problem is encountered while trying to
3419   *                         communicate with the directory server.
3420   */
3421  @Nullable()
3422  public List<String> getMissingAttributeValues(@NotNull final String dn,
3423                           @NotNull final String attributeName,
3424                           @NotNull final Collection<String> attributeValues)
3425       throws LDAPException
3426  {
3427    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3428         attributeValues);
3429  }
3430
3431
3432
3433  /**
3434   * Ensures that the specified entry exists in the directory with all of the
3435   * specified values for the given attribute.  The attribute may or may not
3436   * contain additional values.
3437   * <BR><BR>
3438   * This method may be used regardless of whether the server is listening for
3439   * client connections.
3440   *
3441   * @param  dn               The DN of the entry to examine.
3442   * @param  attributeName    The name of the attribute to examine.
3443   * @param  attributeValues  The set of values which must exist for the given
3444   *                          attribute.
3445   *
3446   * @throws  LDAPException  If a problem is encountered while trying to
3447   *                         communicate with the directory server.
3448   *
3449   * @throws  AssertionError  If the target entry does not exist, does not
3450   *                          contain the specified attribute, or that attribute
3451   *                          does not have all of the specified values.
3452   */
3453  public void assertValueExists(@NotNull final String dn,
3454                                @NotNull final String attributeName,
3455                                @NotNull final String... attributeValues)
3456        throws LDAPException, AssertionError
3457  {
3458    inMemoryHandler.assertValueExists(dn, attributeName,
3459         StaticUtils.toList(attributeValues));
3460  }
3461
3462
3463
3464  /**
3465   * Ensures that the specified entry exists in the directory with all of the
3466   * specified values for the given attribute.  The attribute may or may not
3467   * contain additional values.
3468   * <BR><BR>
3469   * This method may be used regardless of whether the server is listening for
3470   * client connections.
3471   *
3472   * @param  dn               The DN of the entry to examine.
3473   * @param  attributeName    The name of the attribute to examine.
3474   * @param  attributeValues  The set of values which must exist for the given
3475   *                          attribute.
3476   *
3477   * @throws  LDAPException  If a problem is encountered while trying to
3478   *                         communicate with the directory server.
3479   *
3480   * @throws  AssertionError  If the target entry does not exist, does not
3481   *                          contain the specified attribute, or that attribute
3482   *                          does not have all of the specified values.
3483   */
3484  public void assertValueExists(@NotNull final String dn,
3485                   @NotNull final String attributeName,
3486                   @NotNull final Collection<String> attributeValues)
3487        throws LDAPException, AssertionError
3488  {
3489    inMemoryHandler.assertValueExists(dn, attributeName, attributeValues);
3490  }
3491
3492
3493
3494  /**
3495   * Ensures that the specified entry does not exist in the directory.
3496   * <BR><BR>
3497   * This method may be used regardless of whether the server is listening for
3498   * client connections.
3499   *
3500   * @param  dn  The DN of the entry expected to be missing.
3501   *
3502   * @throws  LDAPException  If a problem is encountered while trying to
3503   *                         communicate with the directory server.
3504   *
3505   * @throws  AssertionError  If the target entry is found in the server.
3506   */
3507  public void assertEntryMissing(@NotNull final String dn)
3508         throws LDAPException, AssertionError
3509  {
3510    inMemoryHandler.assertEntryMissing(dn);
3511  }
3512
3513
3514
3515  /**
3516   * Ensures that the specified entry exists in the directory but does not
3517   * contain any of the specified attributes.
3518   * <BR><BR>
3519   * This method may be used regardless of whether the server is listening for
3520   * client connections.
3521   *
3522   * @param  dn              The DN of the entry expected to be present.
3523   * @param  attributeNames  The names of the attributes expected to be missing
3524   *                         from the entry.
3525   *
3526   * @throws  LDAPException  If a problem is encountered while trying to
3527   *                         communicate with the directory server.
3528   *
3529   * @throws  AssertionError  If the target entry is missing from the server, or
3530   *                          if it contains any of the target attributes.
3531   */
3532  public void assertAttributeMissing(@NotNull final String dn,
3533                                     @NotNull final String... attributeNames)
3534         throws LDAPException, AssertionError
3535  {
3536    inMemoryHandler.assertAttributeMissing(dn,
3537         StaticUtils.toList(attributeNames));
3538  }
3539
3540
3541
3542  /**
3543   * Ensures that the specified entry exists in the directory but does not
3544   * contain any of the specified attributes.
3545   * <BR><BR>
3546   * This method may be used regardless of whether the server is listening for
3547   * client connections.
3548   *
3549   * @param  dn              The DN of the entry expected to be present.
3550   * @param  attributeNames  The names of the attributes expected to be missing
3551   *                         from the entry.
3552   *
3553   * @throws  LDAPException  If a problem is encountered while trying to
3554   *                         communicate with the directory server.
3555   *
3556   * @throws  AssertionError  If the target entry is missing from the server, or
3557   *                          if it contains any of the target attributes.
3558   */
3559  public void assertAttributeMissing(@NotNull final String dn,
3560                   @NotNull final Collection<String> attributeNames)
3561         throws LDAPException, AssertionError
3562  {
3563    inMemoryHandler.assertAttributeMissing(dn, attributeNames);
3564  }
3565
3566
3567
3568  /**
3569   * Ensures that the specified entry exists in the directory but does not
3570   * contain any of the specified attribute values.
3571   * <BR><BR>
3572   * This method may be used regardless of whether the server is listening for
3573   * client connections.
3574   *
3575   * @param  dn               The DN of the entry expected to be present.
3576   * @param  attributeName    The name of the attribute to examine.
3577   * @param  attributeValues  The values expected to be missing from the target
3578   *                          entry.
3579   *
3580   * @throws  LDAPException  If a problem is encountered while trying to
3581   *                         communicate with the directory server.
3582   *
3583   * @throws  AssertionError  If the target entry is missing from the server, or
3584   *                          if it contains any of the target attribute values.
3585   */
3586  public void assertValueMissing(@NotNull final String dn,
3587                                 @NotNull final String attributeName,
3588                                 @NotNull final String... attributeValues)
3589         throws LDAPException, AssertionError
3590  {
3591    inMemoryHandler.assertValueMissing(dn, attributeName,
3592         StaticUtils.toList(attributeValues));
3593  }
3594
3595
3596
3597  /**
3598   * Ensures that the specified entry exists in the directory but does not
3599   * contain any of the specified attribute values.
3600   * <BR><BR>
3601   * This method may be used regardless of whether the server is listening for
3602   * client connections.
3603   *
3604   * @param  dn               The DN of the entry expected to be present.
3605   * @param  attributeName    The name of the attribute to examine.
3606   * @param  attributeValues  The values expected to be missing from the target
3607   *                          entry.
3608   *
3609   * @throws  LDAPException  If a problem is encountered while trying to
3610   *                         communicate with the directory server.
3611   *
3612   * @throws  AssertionError  If the target entry is missing from the server, or
3613   *                          if it contains any of the target attribute values.
3614   */
3615  public void assertValueMissing(@NotNull final String dn,
3616                   @NotNull final String attributeName,
3617                   @NotNull final Collection<String> attributeValues)
3618         throws LDAPException, AssertionError
3619  {
3620    inMemoryHandler.assertValueMissing(dn, attributeName, attributeValues);
3621  }
3622}