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