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