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