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