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