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