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