001/*
002 * Copyright 2011-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2011-2017 UnboundID Corp.
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    catch (final Exception e)
1190    {
1191      Debug.debugException(e);
1192      throw new LDAPException(ResultCode.LOCAL_ERROR,
1193           ERR_MEM_DS_INIT_FROM_LDIF_CANNOT_CREATE_READER.get(path,
1194                StaticUtils.getExceptionMessage(e)),
1195           e);
1196    }
1197
1198    return importFromLDIF(clear, reader);
1199  }
1200
1201
1202
1203  /**
1204   * Reads entries from the provided LDIF reader and adds them to the server,
1205   * optionally clearing any existing entries before beginning to add the new
1206   * entries.  If an error is encountered while adding entries from LDIF then
1207   * the server will remain populated with the data it held before the import
1208   * attempt (even if the {@code clear} is given with a value of {@code true}).
1209   * <BR><BR>
1210   * This method may be used regardless of whether the server is listening for
1211   * client connections.
1212   *
1213   * @param  clear   Indicates whether to remove all existing entries prior to
1214   *                 adding entries read from LDIF.
1215   * @param  reader  The LDIF reader to use to obtain the entries to be
1216   *                 imported.
1217   *
1218   * @return  The number of entries read from LDIF and added to the server.
1219   *
1220   * @throws  LDAPException  If a problem occurs while reading entries or adding
1221   *                         them to the server.
1222   */
1223  public int importFromLDIF(final boolean clear, final LDIFReader reader)
1224         throws LDAPException
1225  {
1226    return inMemoryHandler.importFromLDIF(clear, reader);
1227  }
1228
1229
1230
1231  /**
1232   * Writes the current contents of the server in LDIF form to the specified
1233   * file.
1234   * <BR><BR>
1235   * This method may be used regardless of whether the server is listening for
1236   * client connections.
1237   *
1238   * @param  path                   The path of the file to which the LDIF
1239   *                                entries should be written.
1240   * @param  excludeGeneratedAttrs  Indicates whether to exclude automatically
1241   *                                generated operational attributes like
1242   *                                entryUUID, entryDN, creatorsName, etc.
1243   * @param  excludeChangeLog       Indicates whether to exclude entries
1244   *                                contained in the changelog.
1245   *
1246   * @return  The number of entries written to LDIF.
1247   *
1248   * @throws  LDAPException  If a problem occurs while writing entries to LDIF.
1249   */
1250  public int exportToLDIF(final String path,
1251                          final boolean excludeGeneratedAttrs,
1252                          final boolean excludeChangeLog)
1253         throws LDAPException
1254  {
1255    final LDIFWriter ldifWriter;
1256    try
1257    {
1258      ldifWriter = new LDIFWriter(path);
1259    }
1260    catch (final Exception e)
1261    {
1262      Debug.debugException(e);
1263      throw new LDAPException(ResultCode.LOCAL_ERROR,
1264           ERR_MEM_DS_EXPORT_TO_LDIF_CANNOT_CREATE_WRITER.get(path,
1265                StaticUtils.getExceptionMessage(e)),
1266           e);
1267    }
1268
1269    return exportToLDIF(ldifWriter, excludeGeneratedAttrs, excludeChangeLog,
1270         true);
1271  }
1272
1273
1274
1275  /**
1276   * Writes the current contents of the server in LDIF form using the provided
1277   * LDIF writer.
1278   * <BR><BR>
1279   * This method may be used regardless of whether the server is listening for
1280   * client connections.
1281   *
1282   * @param  ldifWriter             The LDIF writer to use when writing the
1283   *                                entries.  It must not be {@code null}.
1284   * @param  excludeGeneratedAttrs  Indicates whether to exclude automatically
1285   *                                generated operational attributes like
1286   *                                entryUUID, entryDN, creatorsName, etc.
1287   * @param  excludeChangeLog       Indicates whether to exclude entries
1288   *                                contained in the changelog.
1289   * @param  closeWriter            Indicates whether the LDIF writer should be
1290   *                                closed after all entries have been written.
1291   *
1292   * @return  The number of entries written to LDIF.
1293   *
1294   * @throws  LDAPException  If a problem occurs while writing entries to LDIF.
1295   */
1296  public int exportToLDIF(final LDIFWriter ldifWriter,
1297                          final boolean excludeGeneratedAttrs,
1298                          final boolean excludeChangeLog,
1299                          final boolean closeWriter)
1300         throws LDAPException
1301  {
1302    return inMemoryHandler.exportToLDIF(ldifWriter, excludeGeneratedAttrs,
1303         excludeChangeLog, closeWriter);
1304  }
1305
1306
1307
1308  /**
1309   * {@inheritDoc}
1310   * <BR><BR>
1311   * This method may be used regardless of whether the server is listening for
1312   * client connections.
1313   */
1314  public RootDSE getRootDSE()
1315         throws LDAPException
1316  {
1317    return new RootDSE(inMemoryHandler.getEntry(""));
1318  }
1319
1320
1321
1322  /**
1323   * {@inheritDoc}
1324   * <BR><BR>
1325   * This method may be used regardless of whether the server is listening for
1326   * client connections.
1327   */
1328  public Schema getSchema()
1329         throws LDAPException
1330  {
1331    return inMemoryHandler.getSchema();
1332  }
1333
1334
1335
1336  /**
1337   * {@inheritDoc}
1338   * <BR><BR>
1339   * This method may be used regardless of whether the server is listening for
1340   * client connections.
1341   */
1342  public Schema getSchema(final String entryDN)
1343         throws LDAPException
1344  {
1345    return inMemoryHandler.getSchema();
1346  }
1347
1348
1349
1350  /**
1351   * {@inheritDoc}
1352   * <BR><BR>
1353   * This method may be used regardless of whether the server is listening for
1354   * client connections.
1355   */
1356  public SearchResultEntry getEntry(final String dn)
1357         throws LDAPException
1358  {
1359    return searchForEntry(dn, SearchScope.BASE,
1360         Filter.createPresenceFilter("objectClass"));
1361  }
1362
1363
1364
1365  /**
1366   * {@inheritDoc}
1367   * <BR><BR>
1368   * This method may be used regardless of whether the server is listening for
1369   * client connections, and regardless of whether search operations are
1370   * allowed in the server.
1371   */
1372  public SearchResultEntry getEntry(final String dn, final String... attributes)
1373         throws LDAPException
1374  {
1375    return searchForEntry(dn, SearchScope.BASE,
1376         Filter.createPresenceFilter("objectClass"), attributes);
1377  }
1378
1379
1380
1381  /**
1382   * {@inheritDoc}
1383   * <BR><BR>
1384   * This method may be used regardless of whether the server is listening for
1385   * client connections, and regardless of whether add operations are allowed in
1386   * the server.
1387   */
1388  public LDAPResult add(final String dn, final Attribute... attributes)
1389         throws LDAPException
1390  {
1391    return add(new AddRequest(dn, attributes));
1392  }
1393
1394
1395
1396  /**
1397   * {@inheritDoc}
1398   * <BR><BR>
1399   * This method may be used regardless of whether the server is listening for
1400   * client connections, and regardless of whether add operations are allowed in
1401   * the server.
1402   */
1403  public LDAPResult add(final String dn, final Collection<Attribute> attributes)
1404         throws LDAPException
1405  {
1406    return add(new AddRequest(dn, attributes));
1407  }
1408
1409
1410
1411  /**
1412   * {@inheritDoc}
1413   * <BR><BR>
1414   * This method may be used regardless of whether the server is listening for
1415   * client connections, and regardless of whether add operations are allowed in
1416   * the server.
1417   */
1418  public LDAPResult add(final Entry entry)
1419         throws LDAPException
1420  {
1421    return add(new AddRequest(entry));
1422  }
1423
1424
1425
1426  /**
1427   * {@inheritDoc}
1428   * <BR><BR>
1429   * This method may be used regardless of whether the server is listening for
1430   * client connections, and regardless of whether add operations are allowed in
1431   * the server.
1432   */
1433  public LDAPResult add(final String... ldifLines)
1434         throws LDIFException, LDAPException
1435  {
1436    return add(new AddRequest(ldifLines));
1437  }
1438
1439
1440
1441  /**
1442   * {@inheritDoc}
1443   * <BR><BR>
1444   * This method may be used regardless of whether the server is listening for
1445   * client connections, and regardless of whether add operations are allowed in
1446   * the server.
1447   */
1448  public LDAPResult add(final AddRequest addRequest)
1449         throws LDAPException
1450  {
1451    final ArrayList<Control> requestControlList =
1452         new ArrayList<Control>(addRequest.getControlList());
1453    requestControlList.add(new Control(
1454         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1455
1456    final LDAPMessage responseMessage = inMemoryHandler.processAddRequest(1,
1457         new AddRequestProtocolOp(addRequest.getDN(),
1458              addRequest.getAttributes()),
1459         requestControlList);
1460
1461    final AddResponseProtocolOp addResponse =
1462         responseMessage.getAddResponseProtocolOp();
1463
1464    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
1465         ResultCode.valueOf(addResponse.getResultCode()),
1466         addResponse.getDiagnosticMessage(), addResponse.getMatchedDN(),
1467         addResponse.getReferralURLs(), responseMessage.getControls());
1468
1469    switch (addResponse.getResultCode())
1470    {
1471      case ResultCode.SUCCESS_INT_VALUE:
1472      case ResultCode.NO_OPERATION_INT_VALUE:
1473        return ldapResult;
1474      default:
1475        throw new LDAPException(ldapResult);
1476    }
1477  }
1478
1479
1480
1481  /**
1482   * {@inheritDoc}
1483   * <BR><BR>
1484   * This method may be used regardless of whether the server is listening for
1485   * client connections, and regardless of whether add operations are allowed in
1486   * the server.
1487   */
1488  public LDAPResult add(final ReadOnlyAddRequest addRequest)
1489         throws LDAPException
1490  {
1491    return add(addRequest.duplicate());
1492  }
1493
1494
1495
1496  /**
1497   * Attempts to add all of the provided entries to the server.  If a problem is
1498   * encountered while attempting to add any of the provided entries, then the
1499   * server will remain populated with the data it held before this method was
1500   * called.
1501   * <BR><BR>
1502   * This method may be used regardless of whether the server is listening for
1503   * client connections, and regardless of whether add operations are allowed in
1504   * the server.
1505   *
1506   * @param  entries  The entries to be added to the server.
1507   *
1508   * @throws  LDAPException  If a problem is encountered while attempting to add
1509   *                         any of the provided entries.
1510   */
1511  public void addEntries(final Entry... entries)
1512         throws LDAPException
1513  {
1514    addEntries(Arrays.asList(entries));
1515  }
1516
1517
1518
1519  /**
1520   * Attempts to add all of the provided entries to the server.  If a problem is
1521   * encountered while attempting to add any of the provided entries, then the
1522   * server will remain populated with the data it held before this method was
1523   * called.
1524   * <BR><BR>
1525   * This method may be used regardless of whether the server is listening for
1526   * client connections, and regardless of whether add operations are allowed in
1527   * the server.
1528   *
1529   * @param  entries  The entries to be added to the server.
1530   *
1531   * @throws  LDAPException  If a problem is encountered while attempting to add
1532   *                         any of the provided entries.
1533   */
1534  public void addEntries(final List<? extends Entry> entries)
1535         throws LDAPException
1536  {
1537    inMemoryHandler.addEntries(entries);
1538  }
1539
1540
1541
1542  /**
1543   * Attempts to add a set of entries provided in LDIF form in which each
1544   * element of the provided array is a line of the LDIF representation, with
1545   * empty strings as separators between entries (as you would have for blank
1546   * lines in an LDIF file).  If a problem is encountered while attempting to
1547   * add any of the provided entries, then the server will remain populated with
1548   * the data it held before this method was called.
1549   * <BR><BR>
1550   * This method may be used regardless of whether the server is listening for
1551   * client connections, and regardless of whether add operations are allowed in
1552   * the server.
1553   *
1554   * @param  ldifEntryLines  The lines comprising the LDIF representation of the
1555   *                         entries to be added.
1556   *
1557   * @throws  LDAPException  If a problem is encountered while attempting to add
1558   *                         any of the provided entries.
1559   */
1560  public void addEntries(final String... ldifEntryLines)
1561         throws LDAPException
1562  {
1563    final ByteStringBuffer buffer = new ByteStringBuffer();
1564    for (final String line : ldifEntryLines)
1565    {
1566      buffer.append(line);
1567      buffer.append(StaticUtils.EOL_BYTES);
1568    }
1569
1570    final ArrayList<Entry> entryList = new ArrayList<Entry>(10);
1571    final LDIFReader reader = new LDIFReader(buffer.asInputStream());
1572    while (true)
1573    {
1574      try
1575      {
1576        final Entry entry = reader.readEntry();
1577        if (entry == null)
1578        {
1579          break;
1580        }
1581        else
1582        {
1583          entryList.add(entry);
1584        }
1585      }
1586      catch (final Exception e)
1587      {
1588        Debug.debugException(e);
1589        throw new LDAPException(ResultCode.PARAM_ERROR,
1590             ERR_MEM_DS_ADD_ENTRIES_LDIF_PARSE_EXCEPTION.get(
1591                  StaticUtils.getExceptionMessage(e)),
1592             e);
1593      }
1594    }
1595
1596    addEntries(entryList);
1597  }
1598
1599
1600
1601  /**
1602   * Processes a simple bind request with the provided DN and password.  Note
1603   * that the bind processing will verify that the provided credentials are
1604   * valid, but it will not alter the server in any way.
1605   *
1606   * @param  bindDN    The bind DN for the bind operation.
1607   * @param  password  The password for the simple bind operation.
1608   *
1609   * @return  The result of processing the bind operation.
1610   *
1611   * @throws  LDAPException  If the server rejects the bind request, or if a
1612   *                         problem occurs while sending the request or reading
1613   *                         the response.
1614   */
1615  public BindResult bind(final String bindDN, final String password)
1616         throws LDAPException
1617  {
1618    return bind(new SimpleBindRequest(bindDN, password));
1619  }
1620
1621
1622
1623  /**
1624   * Processes the provided bind request.  Only simple and SASL PLAIN bind
1625   * requests are supported.  Note that the bind processing will verify that the
1626   * provided credentials are valid, but it will not alter the server in any
1627   * way.
1628   *
1629   * @param  bindRequest  The bind request to be processed.  It must not be
1630   *                      {@code null}.
1631   *
1632   * @return  The result of processing the bind operation.
1633   *
1634   * @throws  LDAPException  If the server rejects the bind request, or if a
1635   *                         problem occurs while sending the request or reading
1636   *                         the response.
1637   */
1638  public BindResult bind(final BindRequest bindRequest)
1639         throws LDAPException
1640  {
1641    final ArrayList<Control> requestControlList =
1642         new ArrayList<Control>(bindRequest.getControlList());
1643    requestControlList.add(new Control(
1644         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1645
1646    final BindRequestProtocolOp bindOp;
1647    if (bindRequest instanceof SimpleBindRequest)
1648    {
1649      final SimpleBindRequest r = (SimpleBindRequest) bindRequest;
1650      bindOp = new BindRequestProtocolOp(r.getBindDN(),
1651           r.getPassword().getValue());
1652    }
1653    else if (bindRequest instanceof PLAINBindRequest)
1654    {
1655      final PLAINBindRequest r = (PLAINBindRequest) bindRequest;
1656
1657      // Create the byte array that should comprise the credentials.
1658      final byte[] authZIDBytes = StaticUtils.getBytes(r.getAuthorizationID());
1659      final byte[] authNIDBytes = StaticUtils.getBytes(r.getAuthenticationID());
1660      final byte[] passwordBytes = r.getPasswordBytes();
1661
1662      final byte[] credBytes = new byte[2 + authZIDBytes.length +
1663           authNIDBytes.length + passwordBytes.length];
1664      System.arraycopy(authZIDBytes, 0, credBytes, 0, authZIDBytes.length);
1665
1666      int pos = authZIDBytes.length + 1;
1667      System.arraycopy(authNIDBytes, 0, credBytes, pos, authNIDBytes.length);
1668
1669      pos += authNIDBytes.length + 1;
1670      System.arraycopy(passwordBytes, 0, credBytes, pos, passwordBytes.length);
1671
1672      bindOp = new BindRequestProtocolOp(null, "PLAIN",
1673           new ASN1OctetString(credBytes));
1674    }
1675    else
1676    {
1677      throw new LDAPException(ResultCode.AUTH_METHOD_NOT_SUPPORTED,
1678           ERR_MEM_DS_UNSUPPORTED_BIND_TYPE.get());
1679    }
1680
1681    final LDAPMessage responseMessage = inMemoryHandler.processBindRequest(1,
1682         bindOp, requestControlList);
1683    final BindResponseProtocolOp bindResponse =
1684         responseMessage.getBindResponseProtocolOp();
1685
1686    final BindResult bindResult = new BindResult(new LDAPResult(
1687         responseMessage.getMessageID(),
1688         ResultCode.valueOf(bindResponse.getResultCode()),
1689         bindResponse.getDiagnosticMessage(), bindResponse.getMatchedDN(),
1690         bindResponse.getReferralURLs(), responseMessage.getControls()));
1691
1692    switch (bindResponse.getResultCode())
1693    {
1694      case ResultCode.SUCCESS_INT_VALUE:
1695        return bindResult;
1696      default:
1697        throw new LDAPException(bindResult);
1698    }
1699  }
1700
1701
1702
1703  /**
1704   * {@inheritDoc}
1705   * <BR><BR>
1706   * This method may be used regardless of whether the server is listening for
1707   * client connections, and regardless of whether compare operations are
1708   * allowed in the server.
1709   */
1710  public CompareResult compare(final String dn, final String attributeName,
1711                        final String assertionValue)
1712         throws LDAPException
1713  {
1714    return compare(new CompareRequest(dn, attributeName, assertionValue));
1715  }
1716
1717
1718
1719  /**
1720   * {@inheritDoc}
1721   * <BR><BR>
1722   * This method may be used regardless of whether the server is listening for
1723   * client connections, and regardless of whether compare operations are
1724   * allowed in the server.
1725   */
1726  public CompareResult compare(final CompareRequest compareRequest)
1727         throws LDAPException
1728  {
1729    final ArrayList<Control> requestControlList =
1730         new ArrayList<Control>(compareRequest.getControlList());
1731    requestControlList.add(new Control(
1732         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1733
1734    final LDAPMessage responseMessage = inMemoryHandler.processCompareRequest(1,
1735         new CompareRequestProtocolOp(compareRequest.getDN(),
1736              compareRequest.getAttributeName(),
1737              compareRequest.getRawAssertionValue()),
1738         requestControlList);
1739
1740    final CompareResponseProtocolOp compareResponse =
1741         responseMessage.getCompareResponseProtocolOp();
1742
1743    final LDAPResult compareResult = new LDAPResult(
1744         responseMessage.getMessageID(),
1745         ResultCode.valueOf(compareResponse.getResultCode()),
1746         compareResponse.getDiagnosticMessage(), compareResponse.getMatchedDN(),
1747         compareResponse.getReferralURLs(), responseMessage.getControls());
1748
1749    switch (compareResponse.getResultCode())
1750    {
1751      case ResultCode.COMPARE_TRUE_INT_VALUE:
1752      case ResultCode.COMPARE_FALSE_INT_VALUE:
1753        return new CompareResult(compareResult);
1754      default:
1755        throw new LDAPException(compareResult);
1756    }
1757  }
1758
1759
1760
1761  /**
1762   * {@inheritDoc}
1763   * <BR><BR>
1764   * This method may be used regardless of whether the server is listening for
1765   * client connections, and regardless of whether compare operations are
1766   * allowed in the server.
1767   */
1768  public CompareResult compare(final ReadOnlyCompareRequest compareRequest)
1769         throws LDAPException
1770  {
1771    return compare(compareRequest.duplicate());
1772  }
1773
1774
1775
1776  /**
1777   * {@inheritDoc}
1778   * <BR><BR>
1779   * This method may be used regardless of whether the server is listening for
1780   * client connections, and regardless of whether delete operations are
1781   * allowed in the server.
1782   */
1783  public LDAPResult delete(final String dn)
1784         throws LDAPException
1785  {
1786    return delete(new DeleteRequest(dn));
1787  }
1788
1789
1790
1791  /**
1792   * {@inheritDoc}
1793   * <BR><BR>
1794   * This method may be used regardless of whether the server is listening for
1795   * client connections, and regardless of whether delete operations are
1796   * allowed in the server.
1797   */
1798  public LDAPResult delete(final DeleteRequest deleteRequest)
1799         throws LDAPException
1800  {
1801    final ArrayList<Control> requestControlList =
1802         new ArrayList<Control>(deleteRequest.getControlList());
1803    requestControlList.add(new Control(
1804         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1805
1806    final LDAPMessage responseMessage = inMemoryHandler.processDeleteRequest(1,
1807         new DeleteRequestProtocolOp(deleteRequest.getDN()),
1808         requestControlList);
1809
1810    final DeleteResponseProtocolOp deleteResponse =
1811         responseMessage.getDeleteResponseProtocolOp();
1812
1813    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
1814         ResultCode.valueOf(deleteResponse.getResultCode()),
1815         deleteResponse.getDiagnosticMessage(), deleteResponse.getMatchedDN(),
1816         deleteResponse.getReferralURLs(), responseMessage.getControls());
1817
1818    switch (deleteResponse.getResultCode())
1819    {
1820      case ResultCode.SUCCESS_INT_VALUE:
1821      case ResultCode.NO_OPERATION_INT_VALUE:
1822        return ldapResult;
1823      default:
1824        throw new LDAPException(ldapResult);
1825    }
1826  }
1827
1828
1829
1830  /**
1831   * {@inheritDoc}
1832   * <BR><BR>
1833   * This method may be used regardless of whether the server is listening for
1834   * client connections, and regardless of whether delete operations are
1835   * allowed in the server.
1836   */
1837  public LDAPResult delete(final ReadOnlyDeleteRequest deleteRequest)
1838         throws LDAPException
1839  {
1840    return delete(deleteRequest.duplicate());
1841  }
1842
1843
1844
1845  /**
1846   * Attempts to delete the specified entry and all entries below it from the
1847   * server.
1848   * <BR><BR>
1849   * This method may be used regardless of whether the server is listening for
1850   * client connections, and regardless of whether compare operations are
1851   * allowed in the server.
1852   *
1853   * @param  baseDN  The DN of the entry to remove, along with all of its
1854   *                 subordinates.
1855   *
1856   * @return  The number of entries removed from the server, or zero if the
1857   *          specified entry was not found.
1858   *
1859   * @throws  LDAPException  If a problem is encountered while attempting to
1860   *                         remove the entries.
1861   */
1862  public int deleteSubtree(final String baseDN)
1863         throws LDAPException
1864  {
1865    return inMemoryHandler.deleteSubtree(baseDN);
1866  }
1867
1868
1869
1870  /**
1871   * Processes an extended request with the provided request OID.  Note that
1872   * because some types of extended operations return unusual result codes under
1873   * "normal" conditions, the server may not always throw an exception for a
1874   * failed extended operation like it does for other types of operations.  It
1875   * will throw an exception under conditions where there appears to be a
1876   * problem with the connection or the server to which the connection is
1877   * established, but there may be many circumstances in which an extended
1878   * operation is not processed correctly but this method does not throw an
1879   * exception.  In the event that no exception is thrown, it is the
1880   * responsibility of the caller to interpret the result to determine whether
1881   * the operation was processed as expected.
1882   * <BR><BR>
1883   * This method may be used regardless of whether the server is listening for
1884   * client connections, and regardless of whether extended operations are
1885   * allowed in the server.
1886   *
1887   * @param  requestOID  The OID for the extended request to process.  It must
1888   *                     not be {@code null}.
1889   *
1890   * @return  The extended result object that provides information about the
1891   *          result of the request processing.  It may or may not indicate that
1892   *          the operation was successful.
1893   *
1894   * @throws  LDAPException  If a problem occurs while sending the request or
1895   *                         reading the response.
1896   */
1897  public ExtendedResult processExtendedOperation(final String requestOID)
1898         throws LDAPException
1899  {
1900    Validator.ensureNotNull(requestOID);
1901
1902    return processExtendedOperation(new ExtendedRequest(requestOID));
1903  }
1904
1905
1906
1907  /**
1908   * Processes an extended request with the provided request OID and value.
1909   * Note that because some types of extended operations return unusual result
1910   * codes under "normal" conditions, the server may not always throw an
1911   * exception for a failed extended operation like it does for other types of
1912   * operations.  It will throw an exception under conditions where there
1913   * appears to be a problem with the connection or the server to which the
1914   * connection is established, but there may be many circumstances in which an
1915   * extended operation is not processed correctly but this method does not
1916   * throw an exception.  In the event that no exception is thrown, it is the
1917   * responsibility of the caller to interpret the result to determine whether
1918   * the operation was processed as expected.
1919   * <BR><BR>
1920   * This method may be used regardless of whether the server is listening for
1921   * client connections, and regardless of whether extended operations are
1922   * allowed in the server.
1923   *
1924   * @param  requestOID    The OID for the extended request to process.  It must
1925   *                       not be {@code null}.
1926   * @param  requestValue  The encoded value for the extended request to
1927   *                       process.  It may be {@code null} if there does not
1928   *                       need to be a value for the requested operation.
1929   *
1930   * @return  The extended result object that provides information about the
1931   *          result of the request processing.  It may or may not indicate that
1932   *          the operation was successful.
1933   *
1934   * @throws  LDAPException  If a problem occurs while sending the request or
1935   *                         reading the response.
1936   */
1937  public ExtendedResult processExtendedOperation(final String requestOID,
1938                             final ASN1OctetString requestValue)
1939         throws LDAPException
1940  {
1941    Validator.ensureNotNull(requestOID);
1942
1943    return processExtendedOperation(new ExtendedRequest(requestOID,
1944         requestValue));
1945  }
1946
1947
1948
1949  /**
1950   * Processes the provided extended request.  Note that because some types of
1951   * extended operations return unusual result codes under "normal" conditions,
1952   * the server may not always throw an exception for a failed extended
1953   * operation like it does for other types of operations.  It will throw an
1954   * exception under conditions where there appears to be a problem with the
1955   * connection or the server to which the connection is established, but there
1956   * may be many circumstances in which an extended operation is not processed
1957   * correctly but this method does not throw an exception.  In the event that
1958   * no exception is thrown, it is the responsibility of the caller to interpret
1959   * the result to determine whether the operation was processed as expected.
1960   * <BR><BR>
1961   * This method may be used regardless of whether the server is listening for
1962   * client connections, and regardless of whether extended operations are
1963   * allowed in the server.
1964   *
1965   * @param  extendedRequest  The extended request to be processed.  It must not
1966   *                          be {@code null}.
1967   *
1968   * @return  The extended result object that provides information about the
1969   *          result of the request processing.  It may or may not indicate that
1970   *          the operation was successful.
1971   *
1972   * @throws  LDAPException  If a problem occurs while sending the request or
1973   *                         reading the response.
1974   */
1975  public ExtendedResult processExtendedOperation(
1976                               final ExtendedRequest extendedRequest)
1977         throws LDAPException
1978  {
1979    Validator.ensureNotNull(extendedRequest);
1980
1981    final ArrayList<Control> requestControlList =
1982         new ArrayList<Control>(extendedRequest.getControlList());
1983    requestControlList.add(new Control(
1984         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
1985
1986
1987    final LDAPMessage responseMessage =
1988         inMemoryHandler.processExtendedRequest(1,
1989              new ExtendedRequestProtocolOp(extendedRequest.getOID(),
1990                   extendedRequest.getValue()),
1991              requestControlList);
1992
1993    final ExtendedResponseProtocolOp extendedResponse =
1994         responseMessage.getExtendedResponseProtocolOp();
1995
1996    final ResultCode rc = ResultCode.valueOf(extendedResponse.getResultCode());
1997
1998    final String[] referralURLs;
1999    final List<String> referralURLList = extendedResponse.getReferralURLs();
2000    if ((referralURLList == null) || referralURLList.isEmpty())
2001    {
2002      referralURLs = StaticUtils.NO_STRINGS;
2003    }
2004    else
2005    {
2006      referralURLs = new String[referralURLList.size()];
2007      referralURLList.toArray(referralURLs);
2008    }
2009
2010    final Control[] responseControls;
2011    final List<Control> controlList = responseMessage.getControls();
2012    if ((controlList == null) || controlList.isEmpty())
2013    {
2014      responseControls = StaticUtils.NO_CONTROLS;
2015    }
2016    else
2017    {
2018      responseControls = new Control[controlList.size()];
2019      controlList.toArray(responseControls);
2020    }
2021
2022    final ExtendedResult extendedResult = new ExtendedResult(
2023         responseMessage.getMessageID(), rc,
2024         extendedResponse.getDiagnosticMessage(),
2025         extendedResponse.getMatchedDN(), referralURLs,
2026         extendedResponse.getResponseOID(),
2027         extendedResponse.getResponseValue(), responseControls);
2028
2029    if ((extendedResult.getOID() == null) &&
2030        (extendedResult.getValue() == null))
2031    {
2032      switch (rc.intValue())
2033      {
2034        case ResultCode.OPERATIONS_ERROR_INT_VALUE:
2035        case ResultCode.PROTOCOL_ERROR_INT_VALUE:
2036        case ResultCode.BUSY_INT_VALUE:
2037        case ResultCode.UNAVAILABLE_INT_VALUE:
2038        case ResultCode.OTHER_INT_VALUE:
2039        case ResultCode.SERVER_DOWN_INT_VALUE:
2040        case ResultCode.LOCAL_ERROR_INT_VALUE:
2041        case ResultCode.ENCODING_ERROR_INT_VALUE:
2042        case ResultCode.DECODING_ERROR_INT_VALUE:
2043        case ResultCode.TIMEOUT_INT_VALUE:
2044        case ResultCode.NO_MEMORY_INT_VALUE:
2045        case ResultCode.CONNECT_ERROR_INT_VALUE:
2046          throw new LDAPException(extendedResult);
2047      }
2048    }
2049
2050    return extendedResult;
2051  }
2052
2053
2054
2055  /**
2056   * {@inheritDoc}
2057   * <BR><BR>
2058   * This method may be used regardless of whether the server is listening for
2059   * client connections, and regardless of whether modify operations are allowed
2060   * in the server.
2061   */
2062  public LDAPResult modify(final String dn, final Modification mod)
2063         throws LDAPException
2064  {
2065    return modify(new ModifyRequest(dn, mod));
2066  }
2067
2068
2069
2070  /**
2071   * {@inheritDoc}
2072   * <BR><BR>
2073   * This method may be used regardless of whether the server is listening for
2074   * client connections, and regardless of whether modify operations are allowed
2075   * in the server.
2076   */
2077  public LDAPResult modify(final String dn, final Modification... mods)
2078         throws LDAPException
2079  {
2080    return modify(new ModifyRequest(dn, mods));
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  public LDAPResult modify(final String dn, final List<Modification> mods)
2093         throws LDAPException
2094  {
2095    return modify(new ModifyRequest(dn, mods));
2096  }
2097
2098
2099
2100  /**
2101   * {@inheritDoc}
2102   * <BR><BR>
2103   * This method may be used regardless of whether the server is listening for
2104   * client connections, and regardless of whether modify operations are allowed
2105   * in the server.
2106   */
2107  public LDAPResult modify(final String... ldifModificationLines)
2108         throws LDIFException, LDAPException
2109  {
2110    return modify(new ModifyRequest(ldifModificationLines));
2111  }
2112
2113
2114
2115  /**
2116   * {@inheritDoc}
2117   * <BR><BR>
2118   * This method may be used regardless of whether the server is listening for
2119   * client connections, and regardless of whether modify operations are allowed
2120   * in the server.
2121   */
2122  public LDAPResult modify(final ModifyRequest modifyRequest)
2123         throws LDAPException
2124  {
2125    final ArrayList<Control> requestControlList =
2126         new ArrayList<Control>(modifyRequest.getControlList());
2127    requestControlList.add(new Control(
2128         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2129
2130    final LDAPMessage responseMessage = inMemoryHandler.processModifyRequest(1,
2131         new ModifyRequestProtocolOp(modifyRequest.getDN(),
2132              modifyRequest.getModifications()),
2133         requestControlList);
2134
2135    final ModifyResponseProtocolOp modifyResponse =
2136         responseMessage.getModifyResponseProtocolOp();
2137
2138    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
2139         ResultCode.valueOf(modifyResponse.getResultCode()),
2140         modifyResponse.getDiagnosticMessage(), modifyResponse.getMatchedDN(),
2141         modifyResponse.getReferralURLs(), responseMessage.getControls());
2142
2143    switch (modifyResponse.getResultCode())
2144    {
2145      case ResultCode.SUCCESS_INT_VALUE:
2146      case ResultCode.NO_OPERATION_INT_VALUE:
2147        return ldapResult;
2148      default:
2149        throw new LDAPException(ldapResult);
2150    }
2151  }
2152
2153
2154
2155  /**
2156   * {@inheritDoc}
2157   * <BR><BR>
2158   * This method may be used regardless of whether the server is listening for
2159   * client connections, and regardless of whether modify operations are allowed
2160   * in the server.
2161   */
2162  public LDAPResult modify(final ReadOnlyModifyRequest modifyRequest)
2163         throws LDAPException
2164  {
2165    return modify(modifyRequest.duplicate());
2166  }
2167
2168
2169
2170  /**
2171   * {@inheritDoc}
2172   * <BR><BR>
2173   * This method may be used regardless of whether the server is listening for
2174   * client connections, and regardless of whether modify DN operations are
2175   * allowed in the server.
2176   */
2177  public LDAPResult modifyDN(final String dn, final String newRDN,
2178                             final boolean deleteOldRDN)
2179         throws LDAPException
2180  {
2181    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
2182  }
2183
2184
2185
2186  /**
2187   * {@inheritDoc}
2188   * <BR><BR>
2189   * This method may be used regardless of whether the server is listening for
2190   * client connections, and regardless of whether modify DN operations are
2191   * allowed in the server.
2192   */
2193  public LDAPResult modifyDN(final String dn, final String newRDN,
2194                             final boolean deleteOldRDN,
2195                             final String newSuperiorDN)
2196         throws LDAPException
2197  {
2198    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
2199         newSuperiorDN));
2200  }
2201
2202
2203
2204  /**
2205   * {@inheritDoc}
2206   * <BR><BR>
2207   * This method may be used regardless of whether the server is listening for
2208   * client connections, and regardless of whether modify DN operations are
2209   * allowed in the server.
2210   */
2211  public LDAPResult modifyDN(final ModifyDNRequest modifyDNRequest)
2212         throws LDAPException
2213  {
2214    final ArrayList<Control> requestControlList =
2215         new ArrayList<Control>(modifyDNRequest.getControlList());
2216    requestControlList.add(new Control(
2217         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2218
2219    final LDAPMessage responseMessage = inMemoryHandler.processModifyDNRequest(
2220         1, new ModifyDNRequestProtocolOp(modifyDNRequest.getDN(),
2221              modifyDNRequest.getNewRDN(), modifyDNRequest.deleteOldRDN(),
2222              modifyDNRequest.getNewSuperiorDN()),
2223         requestControlList);
2224
2225    final ModifyDNResponseProtocolOp modifyDNResponse =
2226         responseMessage.getModifyDNResponseProtocolOp();
2227
2228    final LDAPResult ldapResult = new LDAPResult(responseMessage.getMessageID(),
2229         ResultCode.valueOf(modifyDNResponse.getResultCode()),
2230         modifyDNResponse.getDiagnosticMessage(),
2231         modifyDNResponse.getMatchedDN(), modifyDNResponse.getReferralURLs(),
2232         responseMessage.getControls());
2233
2234    switch (modifyDNResponse.getResultCode())
2235    {
2236      case ResultCode.SUCCESS_INT_VALUE:
2237      case ResultCode.NO_OPERATION_INT_VALUE:
2238        return ldapResult;
2239      default:
2240        throw new LDAPException(ldapResult);
2241    }
2242  }
2243
2244
2245
2246  /**
2247   * {@inheritDoc}
2248   * <BR><BR>
2249   * This method may be used regardless of whether the server is listening for
2250   * client connections, and regardless of whether modify DN operations are
2251   * allowed in the server.
2252   */
2253  public LDAPResult modifyDN(final ReadOnlyModifyDNRequest modifyDNRequest)
2254         throws LDAPException
2255  {
2256    return modifyDN(modifyDNRequest.duplicate());
2257  }
2258
2259
2260
2261  /**
2262   * {@inheritDoc}
2263   * <BR><BR>
2264   * This method may be used regardless of whether the server is listening for
2265   * client connections, and regardless of whether search operations are allowed
2266   * in the server.
2267   */
2268  public SearchResult search(final String baseDN, final SearchScope scope,
2269                             final String filter, final String... attributes)
2270         throws LDAPSearchException
2271  {
2272    return search(new SearchRequest(baseDN, scope, parseFilter(filter),
2273         attributes));
2274  }
2275
2276
2277
2278  /**
2279   * {@inheritDoc}
2280   * <BR><BR>
2281   * This method may be used regardless of whether the server is listening for
2282   * client connections, and regardless of whether search operations are allowed
2283   * in the server.
2284   */
2285  public SearchResult search(final String baseDN, final SearchScope scope,
2286                             final Filter filter, final String... attributes)
2287         throws LDAPSearchException
2288  {
2289    return search(new SearchRequest(baseDN, scope, filter, attributes));
2290  }
2291
2292
2293
2294  /**
2295   * {@inheritDoc}
2296   * <BR><BR>
2297   * This method may be used regardless of whether the server is listening for
2298   * client connections, and regardless of whether search operations are allowed
2299   * in the server.
2300   */
2301  public SearchResult search(final SearchResultListener searchResultListener,
2302                             final String baseDN, final SearchScope scope,
2303                             final String filter, final String... attributes)
2304         throws LDAPSearchException
2305  {
2306    return search(new SearchRequest(searchResultListener, baseDN, scope,
2307         parseFilter(filter), attributes));
2308  }
2309
2310
2311
2312  /**
2313   * {@inheritDoc}
2314   * <BR><BR>
2315   * This method may be used regardless of whether the server is listening for
2316   * client connections, and regardless of whether search operations are allowed
2317   * in the server.
2318   */
2319  public SearchResult search(final SearchResultListener searchResultListener,
2320                             final String baseDN, final SearchScope scope,
2321                             final Filter filter, final String... attributes)
2322         throws LDAPSearchException
2323  {
2324    return search(new SearchRequest(searchResultListener, baseDN, scope,
2325         filter, attributes));
2326  }
2327
2328
2329
2330  /**
2331   * {@inheritDoc}
2332   * <BR><BR>
2333   * This method may be used regardless of whether the server is listening for
2334   * client connections, and regardless of whether search operations are allowed
2335   * in the server.
2336   */
2337  public SearchResult search(final String baseDN, final SearchScope scope,
2338                             final DereferencePolicy derefPolicy,
2339                             final int sizeLimit, final int timeLimit,
2340                             final boolean typesOnly, final String filter,
2341                             final String... attributes)
2342         throws LDAPSearchException
2343  {
2344    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2345         timeLimit, typesOnly, parseFilter(filter), attributes));
2346  }
2347
2348
2349
2350  /**
2351   * {@inheritDoc}
2352   * <BR><BR>
2353   * This method may be used regardless of whether the server is listening for
2354   * client connections, and regardless of whether search operations are allowed
2355   * in the server.
2356   */
2357  public SearchResult search(final String baseDN, final SearchScope scope,
2358                             final DereferencePolicy derefPolicy,
2359                             final int sizeLimit, final int timeLimit,
2360                             final boolean typesOnly, final Filter filter,
2361                             final String... attributes)
2362         throws LDAPSearchException
2363  {
2364    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
2365         timeLimit, typesOnly, filter, attributes));
2366  }
2367
2368
2369
2370  /**
2371   * {@inheritDoc}
2372   * <BR><BR>
2373   * This method may be used regardless of whether the server is listening for
2374   * client connections, and regardless of whether search operations are allowed
2375   * in the server.
2376   */
2377  public SearchResult search(final SearchResultListener searchResultListener,
2378                             final String baseDN, final SearchScope scope,
2379                             final DereferencePolicy derefPolicy,
2380                             final int sizeLimit, final int timeLimit,
2381                             final boolean typesOnly, final String filter,
2382                             final String... attributes)
2383         throws LDAPSearchException
2384  {
2385    return search(new SearchRequest(searchResultListener, baseDN, scope,
2386         derefPolicy, sizeLimit, timeLimit, typesOnly, parseFilter(filter),
2387         attributes));
2388  }
2389
2390
2391
2392  /**
2393   * {@inheritDoc}
2394   * <BR><BR>
2395   * This method may be used regardless of whether the server is listening for
2396   * client connections, and regardless of whether search operations are allowed
2397   * in the server.
2398   */
2399  public SearchResult search(final SearchResultListener searchResultListener,
2400                             final String baseDN, final SearchScope scope,
2401                             final DereferencePolicy derefPolicy,
2402                             final int sizeLimit, final int timeLimit,
2403                             final boolean typesOnly, final Filter filter,
2404                             final String... attributes)
2405         throws LDAPSearchException
2406  {
2407    return search(new SearchRequest(searchResultListener, baseDN, scope,
2408         derefPolicy, sizeLimit, timeLimit, typesOnly, filter, attributes));
2409  }
2410
2411
2412
2413  /**
2414   * {@inheritDoc}
2415   * <BR><BR>
2416   * This method may be used regardless of whether the server is listening for
2417   * client connections, and regardless of whether search operations are allowed
2418   * in the server.
2419   */
2420  public SearchResult search(final SearchRequest searchRequest)
2421         throws LDAPSearchException
2422  {
2423    final ArrayList<Control> requestControlList =
2424         new ArrayList<Control>(searchRequest.getControlList());
2425    requestControlList.add(new Control(
2426         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2427
2428    final List<SearchResultEntry> entryList =
2429         new ArrayList<SearchResultEntry>(10);
2430    final List<SearchResultReference> referenceList =
2431         new ArrayList<SearchResultReference>(10);
2432
2433    final LDAPMessage responseMessage = inMemoryHandler.processSearchRequest(1,
2434         new SearchRequestProtocolOp(searchRequest.getBaseDN(),
2435              searchRequest.getScope(), searchRequest.getDereferencePolicy(),
2436              searchRequest.getSizeLimit(), searchRequest.getTimeLimitSeconds(),
2437              searchRequest.typesOnly(), searchRequest.getFilter(),
2438              searchRequest.getAttributeList()),
2439         requestControlList, entryList, referenceList);
2440
2441
2442    final List<SearchResultEntry> returnEntryList;
2443    final List<SearchResultReference> returnReferenceList;
2444    final SearchResultListener searchListener =
2445         searchRequest.getSearchResultListener();
2446    if (searchListener == null)
2447    {
2448      returnEntryList = Collections.unmodifiableList(entryList);
2449      returnReferenceList = Collections.unmodifiableList(referenceList);
2450    }
2451    else
2452    {
2453      returnEntryList     = null;
2454      returnReferenceList = null;
2455
2456      for (final SearchResultEntry e : entryList)
2457      {
2458        searchListener.searchEntryReturned(e);
2459      }
2460
2461      for (final SearchResultReference r : referenceList)
2462      {
2463        searchListener.searchReferenceReturned(r);
2464      }
2465    }
2466
2467
2468    final SearchResultDoneProtocolOp searchDone =
2469         responseMessage.getSearchResultDoneProtocolOp();
2470
2471    final ResultCode rc = ResultCode.valueOf(searchDone.getResultCode());
2472
2473    final String[] referralURLs;
2474    final List<String> referralURLList = searchDone.getReferralURLs();
2475    if ((referralURLList == null) || referralURLList.isEmpty())
2476    {
2477      referralURLs = StaticUtils.NO_STRINGS;
2478    }
2479    else
2480    {
2481      referralURLs = new String[referralURLList.size()];
2482      referralURLList.toArray(referralURLs);
2483    }
2484
2485    final Control[] responseControls;
2486    final List<Control> controlList = responseMessage.getControls();
2487    if ((controlList == null) || controlList.isEmpty())
2488    {
2489      responseControls = StaticUtils.NO_CONTROLS;
2490    }
2491    else
2492    {
2493      responseControls = new Control[controlList.size()];
2494      controlList.toArray(responseControls);
2495    }
2496
2497    final SearchResult searchResult =new SearchResult(
2498         responseMessage.getMessageID(), rc, searchDone.getDiagnosticMessage(),
2499         searchDone.getMatchedDN(), referralURLs, returnEntryList,
2500         returnReferenceList, entryList.size(), referenceList.size(),
2501         responseControls);
2502
2503    if (rc == ResultCode.SUCCESS)
2504    {
2505      return searchResult;
2506    }
2507    else
2508    {
2509      throw new LDAPSearchException(searchResult);
2510    }
2511  }
2512
2513
2514
2515  /**
2516   * {@inheritDoc}
2517   * <BR><BR>
2518   * This method may be used regardless of whether the server is listening for
2519   * client connections, and regardless of whether search operations are allowed
2520   * in the server.
2521   */
2522  public SearchResult search(final ReadOnlySearchRequest searchRequest)
2523         throws LDAPSearchException
2524  {
2525    return search(searchRequest.duplicate());
2526  }
2527
2528
2529
2530  /**
2531   * {@inheritDoc}
2532   * <BR><BR>
2533   * This method may be used regardless of whether the server is listening for
2534   * client connections, and regardless of whether search operations are allowed
2535   * in the server.
2536   */
2537  public SearchResultEntry searchForEntry(final String baseDN,
2538                                          final SearchScope scope,
2539                                          final String filter,
2540                                          final String... attributes)
2541         throws LDAPSearchException
2542  {
2543    return searchForEntry(new SearchRequest(baseDN, scope, parseFilter(filter),
2544         attributes));
2545  }
2546
2547
2548
2549  /**
2550   * {@inheritDoc}
2551   * <BR><BR>
2552   * This method may be used regardless of whether the server is listening for
2553   * client connections, and regardless of whether search operations are allowed
2554   * in the server.
2555   */
2556  public SearchResultEntry searchForEntry(final String baseDN,
2557                                          final SearchScope scope,
2558                                          final Filter filter,
2559                                          final String... attributes)
2560         throws LDAPSearchException
2561  {
2562    return searchForEntry(new SearchRequest(baseDN, scope, filter, attributes));
2563  }
2564
2565
2566
2567  /**
2568   * {@inheritDoc}
2569   * <BR><BR>
2570   * This method may be used regardless of whether the server is listening for
2571   * client connections, and regardless of whether search operations are allowed
2572   * in the server.
2573   */
2574  public SearchResultEntry searchForEntry(final String baseDN,
2575                                          final SearchScope scope,
2576                                          final DereferencePolicy derefPolicy,
2577                                          final int timeLimit,
2578                                          final boolean typesOnly,
2579                                          final String filter,
2580                                          final String... attributes)
2581         throws LDAPSearchException
2582  {
2583    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2584         timeLimit, typesOnly, parseFilter(filter), attributes));
2585  }
2586
2587
2588
2589  /**
2590   * {@inheritDoc}
2591   * <BR><BR>
2592   * This method may be used regardless of whether the server is listening for
2593   * client connections, and regardless of whether search operations are allowed
2594   * in the server.
2595   */
2596  public SearchResultEntry searchForEntry(final String baseDN,
2597                                          final SearchScope scope,
2598                                          final DereferencePolicy derefPolicy,
2599                                          final int timeLimit,
2600                                          final boolean typesOnly,
2601                                          final Filter filter,
2602                                          final String... attributes)
2603         throws LDAPSearchException
2604  {
2605    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2606         timeLimit, typesOnly, filter, attributes));
2607  }
2608
2609
2610
2611  /**
2612   * {@inheritDoc}
2613   * <BR><BR>
2614   * This method may be used regardless of whether the server is listening for
2615   * client connections, and regardless of whether search operations are allowed
2616   * in the server.
2617   */
2618  public SearchResultEntry searchForEntry(final SearchRequest searchRequest)
2619         throws LDAPSearchException
2620  {
2621    final ArrayList<Control> requestControlList =
2622         new ArrayList<Control>(searchRequest.getControlList());
2623    requestControlList.add(new Control(
2624         InMemoryRequestHandler.OID_INTERNAL_OPERATION_REQUEST_CONTROL, false));
2625
2626    final SearchRequest r;
2627    if ((searchRequest.getSizeLimit() == 1) &&
2628        (searchRequest.getSearchResultListener() == null))
2629    {
2630      r = searchRequest;
2631    }
2632    else
2633    {
2634      r = new SearchRequest(searchRequest.getBaseDN(), searchRequest.getScope(),
2635           searchRequest.getDereferencePolicy(), 1,
2636           searchRequest.getTimeLimitSeconds(), searchRequest.typesOnly(),
2637           searchRequest.getFilter(), searchRequest.getAttributes());
2638
2639      r.setFollowReferrals(InternalSDKHelper.followReferralsInternal(r));
2640      r.setResponseTimeoutMillis(searchRequest.getResponseTimeoutMillis(null));
2641      r.setControls(requestControlList);
2642    }
2643
2644    final SearchResult result;
2645    try
2646    {
2647      result = search(r);
2648    }
2649    catch (final LDAPSearchException lse)
2650    {
2651      Debug.debugException(lse);
2652
2653      if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT)
2654      {
2655        return null;
2656      }
2657
2658      throw lse;
2659    }
2660
2661    if (result.getEntryCount() == 0)
2662    {
2663      return null;
2664    }
2665    else
2666    {
2667      return result.getSearchEntries().get(0);
2668    }
2669  }
2670
2671
2672
2673  /**
2674   * {@inheritDoc}
2675   * <BR><BR>
2676   * This method may be used regardless of whether the server is listening for
2677   * client connections, and regardless of whether search operations are allowed
2678   * in the server.
2679   */
2680  public SearchResultEntry searchForEntry(
2681                                final ReadOnlySearchRequest searchRequest)
2682         throws LDAPSearchException
2683  {
2684    return searchForEntry(searchRequest.duplicate());
2685  }
2686
2687
2688
2689  /**
2690   * Parses the provided string as a search filter.
2691   *
2692   * @param  s  The string to be parsed.
2693   *
2694   * @return  The parsed filter.
2695   *
2696   * @throws  LDAPSearchException  If the provided string could not be parsed as
2697   *                               a valid search filter.
2698   */
2699  private static Filter parseFilter(final String s)
2700          throws LDAPSearchException
2701  {
2702    try
2703    {
2704      return Filter.create(s);
2705    }
2706    catch (final LDAPException le)
2707    {
2708      throw new LDAPSearchException(le);
2709    }
2710  }
2711
2712
2713
2714  /**
2715   * Indicates whether the specified entry exists in the server.
2716   * <BR><BR>
2717   * This method may be used regardless of whether the server is listening for
2718   * client connections.
2719   *
2720   * @param  dn  The DN of the entry for which to make the determination.
2721   *
2722   * @return  {@code true} if the entry exists, or {@code false} if not.
2723   *
2724   * @throws  LDAPException  If a problem is encountered while trying to
2725   *                         communicate with the directory server.
2726   */
2727  public boolean entryExists(final String dn)
2728         throws LDAPException
2729  {
2730    return inMemoryHandler.entryExists(dn);
2731  }
2732
2733
2734
2735  /**
2736   * Indicates whether the specified entry exists in the server and matches the
2737   * given filter.
2738   * <BR><BR>
2739   * This method may be used regardless of whether the server is listening for
2740   * client connections.
2741   *
2742   * @param  dn      The DN of the entry for which to make the determination.
2743   * @param  filter  The filter the entry is expected to match.
2744   *
2745   * @return  {@code true} if the entry exists and matches the specified filter,
2746   *          or {@code false} if not.
2747   *
2748   * @throws  LDAPException  If a problem is encountered while trying to
2749   *                         communicate with the directory server.
2750   */
2751  public boolean entryExists(final String dn, final String filter)
2752         throws LDAPException
2753  {
2754    return inMemoryHandler.entryExists(dn, filter);
2755  }
2756
2757
2758
2759  /**
2760   * Indicates whether the specified entry exists in the server.  This will
2761   * return {@code true} only if the target entry exists and contains all values
2762   * for all attributes of the provided entry.  The entry will be allowed to
2763   * have attribute values not included in the provided entry.
2764   * <BR><BR>
2765   * This method may be used regardless of whether the server is listening for
2766   * client connections.
2767   *
2768   * @param  entry  The entry to compare against the directory server.
2769   *
2770   * @return  {@code true} if the entry exists in the server and is a superset
2771   *          of the provided entry, or {@code false} if not.
2772   *
2773   * @throws  LDAPException  If a problem is encountered while trying to
2774   *                         communicate with the directory server.
2775   */
2776  public boolean entryExists(final Entry entry)
2777         throws LDAPException
2778  {
2779    return inMemoryHandler.entryExists(entry);
2780  }
2781
2782
2783
2784  /**
2785   * Ensures that an entry with the provided DN exists in the directory.
2786   * <BR><BR>
2787   * This method may be used regardless of whether the server is listening for
2788   * client connections.
2789   *
2790   * @param  dn  The DN of the entry for which to make the determination.
2791   *
2792   * @throws  LDAPException  If a problem is encountered while trying to
2793   *                         communicate with the directory server.
2794   *
2795   * @throws  AssertionError  If the target entry does not exist.
2796   */
2797  public void assertEntryExists(final String dn)
2798         throws LDAPException, AssertionError
2799  {
2800    inMemoryHandler.assertEntryExists(dn);
2801  }
2802
2803
2804
2805  /**
2806   * Ensures that an entry with the provided DN exists in the directory.
2807   * <BR><BR>
2808   * This method may be used regardless of whether the server is listening for
2809   * client connections.
2810   *
2811   * @param  dn      The DN of the entry for which to make the determination.
2812   * @param  filter  A filter that the target entry must match.
2813   *
2814   * @throws  LDAPException  If a problem is encountered while trying to
2815   *                         communicate with the directory server.
2816   *
2817   * @throws  AssertionError  If the target entry does not exist or does not
2818   *                          match the provided filter.
2819   */
2820  public void assertEntryExists(final String dn, final String filter)
2821         throws LDAPException, AssertionError
2822  {
2823    inMemoryHandler.assertEntryExists(dn, filter);
2824  }
2825
2826
2827
2828  /**
2829   * Ensures that an entry exists in the directory with the same DN and all
2830   * attribute values contained in the provided entry.  The server entry may
2831   * contain additional attributes and/or attribute values not included in the
2832   * provided entry.
2833   * <BR><BR>
2834   * This method may be used regardless of whether the server is listening for
2835   * client connections.
2836   *
2837   * @param  entry  The entry expected to be present in the directory server.
2838   *
2839   * @throws  LDAPException  If a problem is encountered while trying to
2840   *                         communicate with the directory server.
2841   *
2842   * @throws  AssertionError  If the target entry does not exist or does not
2843   *                          match the provided filter.
2844   */
2845  public void assertEntryExists(final Entry entry)
2846         throws LDAPException, AssertionError
2847  {
2848    inMemoryHandler.assertEntryExists(entry);
2849  }
2850
2851
2852
2853  /**
2854   * Retrieves a list containing the DNs of the entries which are missing from
2855   * the directory server.
2856   * <BR><BR>
2857   * This method may be used regardless of whether the server is listening for
2858   * client connections.
2859   *
2860   * @param  dns  The DNs of the entries to try to find in the server.
2861   *
2862   * @return  A list containing all of the provided DNs that were not found in
2863   *          the server, or an empty list if all entries were found.
2864   *
2865   * @throws  LDAPException  If a problem is encountered while trying to
2866   *                         communicate with the directory server.
2867   */
2868  public List<String> getMissingEntryDNs(final String... dns)
2869         throws LDAPException
2870  {
2871    return inMemoryHandler.getMissingEntryDNs(StaticUtils.toList(dns));
2872  }
2873
2874
2875
2876  /**
2877   * Retrieves a list containing the DNs of the entries which are missing from
2878   * the directory server.
2879   * <BR><BR>
2880   * This method may be used regardless of whether the server is listening for
2881   * client connections.
2882   *
2883   * @param  dns  The DNs of the entries to try to find in the server.
2884   *
2885   * @return  A list containing all of the provided DNs that were not found in
2886   *          the server, or an empty list if all entries were found.
2887   *
2888   * @throws  LDAPException  If a problem is encountered while trying to
2889   *                         communicate with the directory server.
2890   */
2891  public List<String> getMissingEntryDNs(final Collection<String> dns)
2892         throws LDAPException
2893  {
2894    return inMemoryHandler.getMissingEntryDNs(dns);
2895  }
2896
2897
2898
2899  /**
2900   * Ensures that all of the entries with the provided DNs exist in the
2901   * directory.
2902   * <BR><BR>
2903   * This method may be used regardless of whether the server is listening for
2904   * client connections.
2905   *
2906   * @param  dns  The DNs of the entries for which to make the determination.
2907   *
2908   * @throws  LDAPException  If a problem is encountered while trying to
2909   *                         communicate with the directory server.
2910   *
2911   * @throws  AssertionError  If any of the target entries does not exist.
2912   */
2913  public void assertEntriesExist(final String... dns)
2914         throws LDAPException, AssertionError
2915  {
2916    inMemoryHandler.assertEntriesExist(StaticUtils.toList(dns));
2917  }
2918
2919
2920
2921  /**
2922   * Ensures that all of the entries with the provided DNs exist in the
2923   * directory.
2924   * <BR><BR>
2925   * This method may be used regardless of whether the server is listening for
2926   * client connections.
2927   *
2928   * @param  dns  The DNs of the entries for which to make the determination.
2929   *
2930   * @throws  LDAPException  If a problem is encountered while trying to
2931   *                         communicate with the directory server.
2932   *
2933   * @throws  AssertionError  If any of the target entries does not exist.
2934   */
2935  public void assertEntriesExist(final Collection<String> dns)
2936         throws LDAPException, AssertionError
2937  {
2938    inMemoryHandler.assertEntriesExist(dns);
2939  }
2940
2941
2942
2943  /**
2944   * Retrieves a list containing all of the named attributes which do not exist
2945   * in the target entry.
2946   * <BR><BR>
2947   * This method may be used regardless of whether the server is listening for
2948   * client connections.
2949   *
2950   * @param  dn              The DN of the entry to examine.
2951   * @param  attributeNames  The names of the attributes expected to be present
2952   *                         in the target entry.
2953   *
2954   * @return  A list containing the names of the attributes which were not
2955   *          present in the target entry, an empty list if all specified
2956   *          attributes were found in the entry, or {@code null} if the target
2957   *          entry does not exist.
2958   *
2959   * @throws  LDAPException  If a problem is encountered while trying to
2960   *                         communicate with the directory server.
2961   */
2962  public List<String> getMissingAttributeNames(final String dn,
2963                                               final String... attributeNames)
2964         throws LDAPException
2965  {
2966    return inMemoryHandler.getMissingAttributeNames(dn,
2967         StaticUtils.toList(attributeNames));
2968  }
2969
2970
2971
2972  /**
2973   * Retrieves a list containing all of the named attributes which do not exist
2974   * in the target entry.
2975   * <BR><BR>
2976   * This method may be used regardless of whether the server is listening for
2977   * client connections.
2978   *
2979   * @param  dn              The DN of the entry to examine.
2980   * @param  attributeNames  The names of the attributes expected to be present
2981   *                         in the target entry.
2982   *
2983   * @return  A list containing the names of the attributes which were not
2984   *          present in the target entry, an empty list if all specified
2985   *          attributes were found in the entry, or {@code null} if the target
2986   *          entry does not exist.
2987   *
2988   * @throws  LDAPException  If a problem is encountered while trying to
2989   *                         communicate with the directory server.
2990   */
2991  public List<String> getMissingAttributeNames(final String dn,
2992                           final Collection<String> attributeNames)
2993         throws LDAPException
2994  {
2995    return inMemoryHandler.getMissingAttributeNames(dn, attributeNames);
2996  }
2997
2998
2999
3000  /**
3001   * Ensures that the specified entry exists in the directory with all of the
3002   * specified attributes.
3003   * <BR><BR>
3004   * This method may be used regardless of whether the server is listening for
3005   * client connections.
3006   *
3007   * @param  dn              The DN of the entry to examine.
3008   * @param  attributeNames  The names of the attributes that are expected to be
3009   *                         present in the provided entry.
3010   *
3011   * @throws  LDAPException  If a problem is encountered while trying to
3012   *                         communicate with the directory server.
3013   *
3014   * @throws  AssertionError  If the target entry does not exist or does not
3015   *                          contain all of the specified attributes.
3016   */
3017  public void assertAttributeExists(final String dn,
3018                                    final String... attributeNames)
3019        throws LDAPException, AssertionError
3020  {
3021    inMemoryHandler.assertAttributeExists(dn,
3022         StaticUtils.toList(attributeNames));
3023  }
3024
3025
3026
3027  /**
3028   * Ensures that the specified entry exists in the directory with all of the
3029   * specified attributes.
3030   * <BR><BR>
3031   * This method may be used regardless of whether the server is listening for
3032   * client connections.
3033   *
3034   * @param  dn              The DN of the entry to examine.
3035   * @param  attributeNames  The names of the attributes that are expected to be
3036   *                         present in the provided entry.
3037   *
3038   * @throws  LDAPException  If a problem is encountered while trying to
3039   *                         communicate with the directory server.
3040   *
3041   * @throws  AssertionError  If the target entry does not exist or does not
3042   *                          contain all of the specified attributes.
3043   */
3044  public void assertAttributeExists(final String dn,
3045                                    final Collection<String> attributeNames)
3046        throws LDAPException, AssertionError
3047  {
3048    inMemoryHandler.assertAttributeExists(dn, attributeNames);
3049  }
3050
3051
3052
3053  /**
3054   * Retrieves a list of all provided attribute values which are missing from
3055   * the specified entry.
3056   * <BR><BR>
3057   * This method may be used regardless of whether the server is listening for
3058   * client connections.
3059   *
3060   * @param  dn               The DN of the entry to examine.
3061   * @param  attributeName    The attribute expected to be present in the target
3062   *                          entry with the given values.
3063   * @param  attributeValues  The values expected to be present in the target
3064   *                          entry.
3065   *
3066   * @return  A list containing all of the provided values which were not found
3067   *          in the entry, an empty list if all provided attribute values were
3068   *          found, or {@code null} if the target entry does not exist.
3069   *
3070   * @throws  LDAPException  If a problem is encountered while trying to
3071   *                         communicate with the directory server.
3072   */
3073  public List<String> getMissingAttributeValues(final String dn,
3074                                                final String attributeName,
3075                                                final String... attributeValues)
3076         throws LDAPException
3077  {
3078    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3079         StaticUtils.toList(attributeValues));
3080  }
3081
3082
3083
3084  /**
3085   * Retrieves a list of all provided attribute values which are missing from
3086   * the specified entry.  The target attribute may or may not contain
3087   * additional values.
3088   * <BR><BR>
3089   * This method may be used regardless of whether the server is listening for
3090   * client connections.
3091   *
3092   * @param  dn               The DN of the entry to examine.
3093   * @param  attributeName    The attribute expected to be present in the target
3094   *                          entry with the given values.
3095   * @param  attributeValues  The values expected to be present in the target
3096   *                          entry.
3097   *
3098   * @return  A list containing all of the provided values which were not found
3099   *          in the entry, an empty list if all provided attribute values were
3100   *          found, or {@code null} if the target entry does not exist.
3101   *
3102   * @throws  LDAPException  If a problem is encountered while trying to
3103   *                         communicate with the directory server.
3104   */
3105  public List<String> getMissingAttributeValues(final String dn,
3106                           final String attributeName,
3107                           final Collection<String> attributeValues)
3108       throws LDAPException
3109  {
3110    return inMemoryHandler.getMissingAttributeValues(dn, attributeName,
3111         attributeValues);
3112  }
3113
3114
3115
3116  /**
3117   * Ensures that the specified entry exists in the directory with all of the
3118   * specified values for the given attribute.  The attribute may or may not
3119   * contain additional values.
3120   * <BR><BR>
3121   * This method may be used regardless of whether the server is listening for
3122   * client connections.
3123   *
3124   * @param  dn               The DN of the entry to examine.
3125   * @param  attributeName    The name of the attribute to examine.
3126   * @param  attributeValues  The set of values which must exist for the given
3127   *                          attribute.
3128   *
3129   * @throws  LDAPException  If a problem is encountered while trying to
3130   *                         communicate with the directory server.
3131   *
3132   * @throws  AssertionError  If the target entry does not exist, does not
3133   *                          contain the specified attribute, or that attribute
3134   *                          does not have all of the specified values.
3135   */
3136  public void assertValueExists(final String dn, final String attributeName,
3137                                final String... attributeValues)
3138        throws LDAPException, AssertionError
3139  {
3140    inMemoryHandler.assertValueExists(dn, attributeName,
3141         StaticUtils.toList(attributeValues));
3142  }
3143
3144
3145
3146  /**
3147   * Ensures that the specified entry exists in the directory with all of the
3148   * specified values for the given attribute.  The attribute may or may not
3149   * contain additional values.
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  attributeName    The name of the attribute to examine.
3156   * @param  attributeValues  The set of values which must exist for the given
3157   *                          attribute.
3158   *
3159   * @throws  LDAPException  If a problem is encountered while trying to
3160   *                         communicate with the directory server.
3161   *
3162   * @throws  AssertionError  If the target entry does not exist, does not
3163   *                          contain the specified attribute, or that attribute
3164   *                          does not have all of the specified values.
3165   */
3166  public void assertValueExists(final String dn, final String attributeName,
3167                                final Collection<String> attributeValues)
3168        throws LDAPException, AssertionError
3169  {
3170    inMemoryHandler.assertValueExists(dn, attributeName, attributeValues);
3171  }
3172
3173
3174
3175  /**
3176   * Ensures that the specified entry does not exist in the directory.
3177   * <BR><BR>
3178   * This method may be used regardless of whether the server is listening for
3179   * client connections.
3180   *
3181   * @param  dn  The DN of the entry expected to be missing.
3182   *
3183   * @throws  LDAPException  If a problem is encountered while trying to
3184   *                         communicate with the directory server.
3185   *
3186   * @throws  AssertionError  If the target entry is found in the server.
3187   */
3188  public void assertEntryMissing(final String dn)
3189         throws LDAPException, AssertionError
3190  {
3191    inMemoryHandler.assertEntryMissing(dn);
3192  }
3193
3194
3195
3196  /**
3197   * Ensures that the specified entry exists in the directory but does not
3198   * contain any of the specified attributes.
3199   * <BR><BR>
3200   * This method may be used regardless of whether the server is listening for
3201   * client connections.
3202   *
3203   * @param  dn              The DN of the entry expected to be present.
3204   * @param  attributeNames  The names of the attributes expected to be missing
3205   *                         from the entry.
3206   *
3207   * @throws  LDAPException  If a problem is encountered while trying to
3208   *                         communicate with the directory server.
3209   *
3210   * @throws  AssertionError  If the target entry is missing from the server, or
3211   *                          if it contains any of the target attributes.
3212   */
3213  public void assertAttributeMissing(final String dn,
3214                                     final String... attributeNames)
3215         throws LDAPException, AssertionError
3216  {
3217    inMemoryHandler.assertAttributeMissing(dn,
3218         StaticUtils.toList(attributeNames));
3219  }
3220
3221
3222
3223  /**
3224   * Ensures that the specified entry exists in the directory but does not
3225   * contain any of the specified attributes.
3226   * <BR><BR>
3227   * This method may be used regardless of whether the server is listening for
3228   * client connections.
3229   *
3230   * @param  dn              The DN of the entry expected to be present.
3231   * @param  attributeNames  The names of the attributes expected to be missing
3232   *                         from the entry.
3233   *
3234   * @throws  LDAPException  If a problem is encountered while trying to
3235   *                         communicate with the directory server.
3236   *
3237   * @throws  AssertionError  If the target entry is missing from the server, or
3238   *                          if it contains any of the target attributes.
3239   */
3240  public void assertAttributeMissing(final String dn,
3241                                     final Collection<String> attributeNames)
3242         throws LDAPException, AssertionError
3243  {
3244    inMemoryHandler.assertAttributeMissing(dn, attributeNames);
3245  }
3246
3247
3248
3249  /**
3250   * Ensures that the specified entry exists in the directory but does not
3251   * contain any of the specified attribute values.
3252   * <BR><BR>
3253   * This method may be used regardless of whether the server is listening for
3254   * client connections.
3255   *
3256   * @param  dn               The DN of the entry expected to be present.
3257   * @param  attributeName    The name of the attribute to examine.
3258   * @param  attributeValues  The values expected to be missing from the target
3259   *                          entry.
3260   *
3261   * @throws  LDAPException  If a problem is encountered while trying to
3262   *                         communicate with the directory server.
3263   *
3264   * @throws  AssertionError  If the target entry is missing from the server, or
3265   *                          if it contains any of the target attribute values.
3266   */
3267  public void assertValueMissing(final String dn, final String attributeName,
3268                                 final String... attributeValues)
3269         throws LDAPException, AssertionError
3270  {
3271    inMemoryHandler.assertValueMissing(dn, attributeName,
3272         StaticUtils.toList(attributeValues));
3273  }
3274
3275
3276
3277  /**
3278   * Ensures that the specified entry exists in the directory but does not
3279   * contain any of the specified attribute values.
3280   * <BR><BR>
3281   * This method may be used regardless of whether the server is listening for
3282   * client connections.
3283   *
3284   * @param  dn               The DN of the entry expected to be present.
3285   * @param  attributeName    The name of the attribute to examine.
3286   * @param  attributeValues  The values expected to be missing from the target
3287   *                          entry.
3288   *
3289   * @throws  LDAPException  If a problem is encountered while trying to
3290   *                         communicate with the directory server.
3291   *
3292   * @throws  AssertionError  If the target entry is missing from the server, or
3293   *                          if it contains any of the target attribute values.
3294   */
3295  public void assertValueMissing(final String dn, final String attributeName,
3296                                 final Collection<String> attributeValues)
3297         throws LDAPException, AssertionError
3298  {
3299    inMemoryHandler.assertValueMissing(dn, attributeName, attributeValues);
3300  }
3301}