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