001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.sdk;
037
038
039
040import java.io.Closeable;
041import java.util.ArrayList;
042import java.util.Collection;
043import java.util.EnumSet;
044import java.util.List;
045import java.util.Set;
046import java.util.concurrent.TimeUnit;
047import java.util.concurrent.TimeoutException;
048
049import com.unboundid.asn1.ASN1OctetString;
050import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
051import com.unboundid.ldap.sdk.schema.Schema;
052import com.unboundid.ldif.LDIFException;
053import com.unboundid.util.Debug;
054import com.unboundid.util.NotExtensible;
055import com.unboundid.util.NotNull;
056import com.unboundid.util.Nullable;
057import com.unboundid.util.StaticUtils;
058import com.unboundid.util.ThreadSafety;
059import com.unboundid.util.ThreadSafetyLevel;
060import com.unboundid.util.Validator;
061
062import static com.unboundid.ldap.sdk.LDAPMessages.*;
063
064
065
066/**
067 * This class provides the base class for LDAP connection pool implementations
068 * provided by the LDAP SDK for Java.
069 */
070@NotExtensible()
071@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE)
072public abstract class AbstractConnectionPool
073       implements FullLDAPInterface, Closeable
074{
075  /**
076   * Closes this connection pool.  All connections currently held in the pool
077   * that are not in use will be closed, and any outstanding connections will be
078   * automatically closed when they are released back to the pool.
079   */
080  @Override()
081  public abstract void close();
082
083
084
085  /**
086   * Closes this connection pool, optionally using multiple threads to close the
087   * connections in parallel.
088   *
089   * @param  unbind      Indicates whether to try to send an unbind request to
090   *                     the server before closing the connection.
091   * @param  numThreads  The number of threads to use when closing the
092   *                     connections.
093   */
094  public abstract void close(boolean unbind, int numThreads);
095
096
097
098  /**
099   * Indicates whether this connection pool has been closed.
100   *
101   * @return  {@code true} if this connection pool has been closed, or
102   *          {@code false} if not.
103   */
104  public abstract boolean isClosed();
105
106
107
108  /**
109   * Retrieves an LDAP connection from the pool.
110   *
111   * @return  The LDAP connection taken from the pool.
112   *
113   * @throws  LDAPException  If no connection is available, or a problem occurs
114   *                         while creating a new connection to return.
115   */
116  @NotNull()
117  public abstract LDAPConnection getConnection()
118         throws LDAPException;
119
120
121
122  /**
123   * Releases the provided connection back to this pool.
124   *
125   * @param  connection  The connection to be released back to the pool.
126   */
127  public abstract void releaseConnection(@NotNull LDAPConnection connection);
128
129
130
131  /**
132   * Indicates that the provided connection is no longer in use, but is also no
133   * longer fit for use.  The provided connection will be terminated and a new
134   * connection will be created and added to the pool in its place.
135   *
136   * @param  connection  The defunct connection being released.
137   */
138  public abstract void releaseDefunctConnection(
139                            @NotNull LDAPConnection connection);
140
141
142
143  /**
144   * Releases the provided connection back to the pool after an exception has
145   * been encountered while processing an operation on that connection.  The
146   * connection pool health check instance associated with this pool will be
147   * used to determine whether the provided connection is still valid and will
148   * either release it back for use in processing other operations on the
149   * connection or will terminate the connection and create a new one to take
150   * its place.
151   *
152   * @param  connection  The connection to be evaluated and released back to the
153   *                     pool or replaced with a new connection.
154   * @param  exception   The exception caught while processing an operation on
155   *                     the connection.
156   */
157  public final void releaseConnectionAfterException(
158                         @NotNull final LDAPConnection connection,
159                         @NotNull final LDAPException exception)
160  {
161    final LDAPConnectionPoolHealthCheck healthCheck = getHealthCheck();
162
163    try
164    {
165      healthCheck.ensureConnectionValidAfterException(connection, exception);
166      releaseConnection(connection);
167    }
168    catch (final LDAPException le)
169    {
170      Debug.debugException(le);
171      releaseDefunctConnection(connection);
172    }
173  }
174
175
176
177  /**
178   * Releases the provided connection as defunct and creates a new connection to
179   * replace it, if possible, optionally connected to a different directory
180   * server instance than the instance with which the original connection was
181   * established.
182   *
183   * @param  connection  The defunct connection to be replaced.
184   *
185   * @return  The newly-created connection intended to replace the provided
186   *          connection.
187   *
188   * @throws  LDAPException  If a problem is encountered while trying to create
189   *                         the new connection.  Note that even if an exception
190   *                         is thrown, then the provided connection must have
191   *                         been properly released as defunct.
192   */
193  @NotNull()
194  public abstract LDAPConnection replaceDefunctConnection(
195                                      @NotNull LDAPConnection connection)
196         throws LDAPException;
197
198
199
200  /**
201   * Attempts to replace the provided connection.  However, if an exception is
202   * encountered while obtaining the new connection then an exception will be
203   * thrown based on the provided {@code Throwable} object.
204   *
205   * @param  t           The {@code Throwable} that was caught and prompted the
206   *                     connection to be replaced.
207   * @param  connection  The defunct connection to be replaced.
208   *
209   * @return  The newly-created connection intended to replace the provided
210   *          connection.
211   *
212   * @throws  LDAPException  If an exception is encountered while attempting to
213   *                         obtain the new connection.  Note that this
214   *                         exception will be generated from the provided
215   *                         {@code Throwable} rather than based on the
216   *                         exception caught while trying to create the new
217   *                         connection.
218   */
219  @NotNull()
220  private LDAPConnection replaceDefunctConnection(@NotNull final Throwable t,
221                              @NotNull final LDAPConnection connection)
222          throws LDAPException
223  {
224    try
225    {
226      return replaceDefunctConnection(connection);
227    }
228    catch (final LDAPException le)
229    {
230      Debug.debugException(le);
231
232      if (t instanceof LDAPException)
233      {
234        throw (LDAPException) t;
235      }
236      else
237      {
238        throw new LDAPException(ResultCode.LOCAL_ERROR,
239             ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t);
240      }
241    }
242  }
243
244
245
246  /**
247   * Indicates whether attempts to process operations should be retried on a
248   * newly-created connection if the initial attempt fails in a manner that
249   * indicates that the connection used to process that request may no longer
250   * be valid.  Only a single retry will be attempted for any operation.
251   * <BR><BR>
252   * Note that this only applies to methods used to process operations in the
253   * context pool (e.g., using methods that are part of {@link LDAPInterface}),
254   * and will not automatically be used for operations processed on connections
255   * checked out of the pool.
256   * <BR><BR>
257   * This method is provided for the purpose of backward compatibility, but new
258   * functionality has been added to control retry on a per-operation-type
259   * basis via the {@link #setRetryFailedOperationsDueToInvalidConnections(Set)}
260   * method.  If retry is enabled for any operation type, then this method will
261   * return {@code true}, and it will only return {@code false} if retry should
262   * not be used for any operation type.  To determine the operation types for
263   * which failed operations may be retried, use the
264   * {@link #getOperationTypesToRetryDueToInvalidConnections()}  method.
265   *
266   * @return  {@code true} if the connection pool should attempt to retry
267   *          operations on a newly-created connection if they fail in a way
268   *          that indicates the associated connection may no longer be usable,
269   *          or {@code false} if operations should only be attempted once.
270   */
271  public final boolean retryFailedOperationsDueToInvalidConnections()
272  {
273    return (! getOperationTypesToRetryDueToInvalidConnections().isEmpty());
274  }
275
276
277
278  /**
279   * Retrieves the set of operation types for which operations should be
280   * retried if the initial attempt fails in a manner that indicates that the
281   * connection used to process the request may no longer be valid.
282   *
283   * @return  The set of operation types for which operations should be
284   *          retried if the initial attempt fails in a manner that indicates
285   *          that the connection used to process the request may no longer be
286   *          valid, or an empty set if retries should not be performed for any
287   *          type of operation.
288   */
289  @NotNull()
290  public abstract Set<OperationType>
291              getOperationTypesToRetryDueToInvalidConnections();
292
293
294
295  /**
296   * Specifies whether attempts to process operations should be retried on a
297   * newly-created connection if the initial attempt fails in a manner that
298   * indicates that the connection used to process that request may no longer
299   * be valid.  Only a single retry will be attempted for any operation.
300   * <BR><BR>
301   * Note that this only applies to methods used to process operations in the
302   * context pool (e.g., using methods that are part of {@link LDAPInterface}),
303   * and will not automatically be used for operations processed on connections
304   * checked out of the pool.
305   * <BR><BR>
306   * This method is provided for the purpose of backward compatibility, but new
307   * functionality has been added to control retry on a per-operation-type
308   * basis via the {@link #setRetryFailedOperationsDueToInvalidConnections(Set)}
309   * method.  If this is called with a value of {@code true}, then retry will be
310   * enabled for all types of operations.  If it is called with a value of
311   * {@code false}, then retry will be disabled for all types of operations.
312   *
313   * @param  retryFailedOperationsDueToInvalidConnections
314   *              Indicates whether attempts to process operations should be
315   *              retried on a newly-created connection if they fail in a way
316   *              that indicates the associated connection may no longer be
317   *              usable.
318   */
319  public final void setRetryFailedOperationsDueToInvalidConnections(
320              final boolean retryFailedOperationsDueToInvalidConnections)
321  {
322    if (retryFailedOperationsDueToInvalidConnections)
323    {
324      setRetryFailedOperationsDueToInvalidConnections(
325           EnumSet.allOf(OperationType.class));
326    }
327    else
328    {
329      setRetryFailedOperationsDueToInvalidConnections(
330           EnumSet.noneOf(OperationType.class));
331    }
332  }
333
334
335
336  /**
337   * Specifies the types of operations that should be retried on a newly-created
338   * connection if the initial attempt fails in a manner that indicates that
339   * the connection used to process the request may no longer be valid.  Only a
340   * single retry will be attempted for any operation.
341   * <BR><BR>
342   * Note that this only applies to methods used to process operations in the
343   * context pool (e.g., using methods that are part of {@link LDAPInterface}),
344   * and will not automatically be used for operations processed on connections
345   * checked out of the pool.
346   *
347   * @param  operationTypes  The types of operations for which to retry failed
348   *                         operations if they fail in a way that indicates the
349   *                         associated connection may no longer be usable.  It
350   *                         may be {@code null} or empty to indicate that no
351   *                         types of operations should be retried.
352   */
353  public abstract void setRetryFailedOperationsDueToInvalidConnections(
354                            @Nullable Set<OperationType> operationTypes);
355
356
357
358  /**
359   * Retrieves the number of connections that are currently available for use in
360   * this connection pool, if applicable.
361   *
362   * @return  The number of connections that are currently available for use in
363   *          this connection pool, or -1 if that is not applicable for this
364   *          type of connection pool.
365   */
366  public abstract int getCurrentAvailableConnections();
367
368
369
370  /**
371   * Retrieves the maximum number of connections to be maintained in this
372   * connection pool, which is the maximum number of available connections that
373   * should be available at any time, if applicable.
374   *
375   * @return  The number of connections to be maintained in this connection
376   *          pool, or -1 if that is not applicable for this type of connection
377   *          pool.
378   */
379  public abstract int getMaximumAvailableConnections();
380
381
382
383  /**
384   * Retrieves the set of statistics maintained for this LDAP connection pool.
385   *
386   * @return  The set of statistics maintained for this LDAP connection pool.
387   */
388  @NotNull()
389  public abstract LDAPConnectionPoolStatistics getConnectionPoolStatistics();
390
391
392
393  /**
394   * Retrieves the user-friendly name that has been assigned to this connection
395   * pool.
396   *
397   * @return  The user-friendly name that has been assigned to this connection
398   *          pool, or {@code null} if none has been assigned.
399   */
400  @Nullable()
401  public abstract String getConnectionPoolName();
402
403
404
405  /**
406   * Specifies the user-friendly name that should be used for this connection
407   * pool.  This name may be used in debugging to help identify the purpose of
408   * this connection pool.  It will also be assigned to all connections
409   * associated with this connection pool.
410   *
411   * @param  connectionPoolName  The user-friendly name that should be used for
412   *                             this connection pool.
413   */
414  public abstract void setConnectionPoolName(
415                            @Nullable String connectionPoolName);
416
417
418
419  /**
420   * Retrieves the health check implementation for this connection pool.
421   *
422   * @return  The health check implementation for this connection pool.
423   */
424  @NotNull()
425  public abstract LDAPConnectionPoolHealthCheck getHealthCheck();
426
427
428
429  /**
430   * Retrieves the length of time in milliseconds between periodic background
431   * health checks against the available connections in this pool.
432   *
433   * @return  The length of time in milliseconds between the periodic background
434   *          health checks against the available connections in this pool.
435   */
436  public abstract long getHealthCheckIntervalMillis();
437
438
439
440  /**
441   * Specifies the length of time in milliseconds between periodic background
442   * health checks against the available connections in this pool.
443   *
444   * @param  healthCheckInterval  The length of time in milliseconds between
445   *                              periodic background health checks against the
446   *                              available connections in this pool.  The
447   *                              provided value must be greater than zero.
448   */
449  public abstract void setHealthCheckIntervalMillis(long healthCheckInterval);
450
451
452
453  /**
454   * Performs a health check against all connections currently available in this
455   * connection pool.  This should only be invoked by the connection pool health
456   * check thread.
457   */
458  protected abstract void doHealthCheck();
459
460
461
462  /**
463   * Retrieves the directory server root DSE using a connection from this
464   * connection pool.
465   *
466   * @return  The directory server root DSE, or {@code null} if it is not
467   *          available.
468   *
469   * @throws  LDAPException  If a problem occurs while attempting to retrieve
470   *                         the server root DSE.
471   */
472  @Override()
473  @Nullable()
474  public final RootDSE getRootDSE()
475         throws LDAPException
476  {
477    final LDAPConnection conn = getConnection();
478
479    try
480    {
481      final RootDSE rootDSE = conn.getRootDSE();
482      releaseConnection(conn);
483      return rootDSE;
484    }
485    catch (final Throwable t)
486    {
487      throwLDAPExceptionIfShouldNotRetry(t, OperationType.SEARCH, conn);
488
489      // If we have gotten here, then we should retry the operation with a
490      // newly-created connection.
491      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
492
493      try
494      {
495        final RootDSE rootDSE = newConn.getRootDSE();
496        releaseConnection(newConn);
497        return rootDSE;
498      }
499      catch (final Throwable t2)
500      {
501        throwLDAPException(t2, newConn);
502      }
503
504      // This return statement should never be reached.
505      return null;
506    }
507  }
508
509
510
511  /**
512   * Retrieves the directory server schema definitions using a connection from
513   * this connection pool, using the subschema subentry DN contained in the
514   * server's root DSE.  For directory servers containing a single schema, this
515   * should be sufficient for all purposes.  For servers with multiple schemas,
516   * it may be necessary to specify the DN of the target entry for which to
517   * obtain the associated schema.
518   *
519   * @return  The directory server schema definitions, or {@code null} if the
520   *          schema information could not be retrieved (e.g, the client does
521   *          not have permission to read the server schema).
522   *
523   * @throws  LDAPException  If a problem occurs while attempting to retrieve
524   *                         the server schema.
525   */
526  @Override()
527  @Nullable()
528  public final Schema getSchema()
529         throws LDAPException
530  {
531    return getSchema("");
532  }
533
534
535
536  /**
537   * Retrieves the directory server schema definitions that govern the specified
538   * entry using a connection from this connection pool.  The subschemaSubentry
539   * attribute will be retrieved from the target entry, and then the appropriate
540   * schema definitions will be loaded from the entry referenced by that
541   * attribute.  This may be necessary to ensure correct behavior in servers
542   * that support multiple schemas.
543   *
544   * @param  entryDN  The DN of the entry for which to retrieve the associated
545   *                  schema definitions.  It may be {@code null} or an empty
546   *                  string if the subschemaSubentry attribute should be
547   *                  retrieved from the server's root DSE.
548   *
549   * @return  The directory server schema definitions, or {@code null} if the
550   *          schema information could not be retrieved (e.g, the client does
551   *          not have permission to read the server schema).
552   *
553   * @throws  LDAPException  If a problem occurs while attempting to retrieve
554   *                         the server schema.
555   */
556  @Override()
557  @Nullable()
558  public final Schema getSchema(@Nullable final String entryDN)
559         throws LDAPException
560  {
561    final LDAPConnection conn = getConnection();
562
563    try
564    {
565      final Schema schema = conn.getSchema(entryDN);
566      releaseConnection(conn);
567      return schema;
568    }
569    catch (final Throwable t)
570    {
571      throwLDAPExceptionIfShouldNotRetry(t, OperationType.SEARCH, conn);
572
573      // If we have gotten here, then we should retry the operation with a
574      // newly-created connection.
575      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
576
577      try
578      {
579        final Schema schema = newConn.getSchema(entryDN);
580        releaseConnection(newConn);
581        return schema;
582      }
583      catch (final Throwable t2)
584      {
585        throwLDAPException(t2, newConn);
586      }
587
588      // This return statement should never be reached.
589      return null;
590    }
591  }
592
593
594
595  /**
596   * Retrieves the entry with the specified DN using a connection from this
597   * connection pool.  All user attributes will be requested in the entry to
598   * return.
599   *
600   * @param  dn  The DN of the entry to retrieve.  It must not be {@code null}.
601   *
602   * @return  The requested entry, or {@code null} if the target entry does not
603   *          exist or no entry was returned (e.g., if the authenticated user
604   *          does not have permission to read the target entry).
605   *
606   * @throws  LDAPException  If a problem occurs while sending the request or
607   *                         reading the response.
608   */
609  @Override()
610  @Nullable()
611  public final SearchResultEntry getEntry(@NotNull final String dn)
612         throws LDAPException
613  {
614    return getEntry(dn, StaticUtils.NO_STRINGS);
615  }
616
617
618
619  /**
620   * Retrieves the entry with the specified DN using a connection from this
621   * connection pool.
622   *
623   * @param  dn          The DN of the entry to retrieve.  It must not be
624   *                     {@code null}.
625   * @param  attributes  The set of attributes to request for the target entry.
626   *                     If it is {@code null}, then all user attributes will be
627   *                     requested.
628   *
629   * @return  The requested entry, or {@code null} if the target entry does not
630   *          exist or no entry was returned (e.g., if the authenticated user
631   *          does not have permission to read the target entry).
632   *
633   * @throws  LDAPException  If a problem occurs while sending the request or
634   *                         reading the response.
635   */
636  @Override()
637  @Nullable()
638  public final SearchResultEntry getEntry(@NotNull final String dn,
639                                          @Nullable final String... attributes)
640         throws LDAPException
641  {
642    final LDAPConnection conn = getConnection();
643
644    try
645    {
646      final SearchResultEntry entry = conn.getEntry(dn, attributes);
647      releaseConnection(conn);
648      return entry;
649    }
650    catch (final Throwable t)
651    {
652      throwLDAPExceptionIfShouldNotRetry(t, OperationType.SEARCH, conn);
653
654      // If we have gotten here, then we should retry the operation with a
655      // newly-created connection.
656      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
657
658      try
659      {
660        final SearchResultEntry entry = newConn.getEntry(dn, attributes);
661        releaseConnection(newConn);
662        return entry;
663      }
664      catch (final Throwable t2)
665      {
666        throwLDAPException(t2, newConn);
667      }
668
669      // This return statement should never be reached.
670      return null;
671    }
672  }
673
674
675
676  /**
677   * Processes an add operation with the provided information using a connection
678   * from this connection pool.
679   *
680   * @param  dn          The DN of the entry to add.  It must not be
681   *                     {@code null}.
682   * @param  attributes  The set of attributes to include in the entry to add.
683   *                     It must not be {@code null}.
684   *
685   * @return  The result of processing the add operation.
686   *
687   * @throws  LDAPException  If the server rejects the add request, or if a
688   *                         problem is encountered while sending the request or
689   *                         reading the response.
690   */
691  @Override()
692  @NotNull()
693  public final LDAPResult add(@NotNull final String dn,
694                              @NotNull final Attribute... attributes)
695         throws LDAPException
696  {
697    return add(new AddRequest(dn, attributes));
698  }
699
700
701
702  /**
703   * Processes an add operation with the provided information using a connection
704   * from this connection pool.
705   *
706   * @param  dn          The DN of the entry to add.  It must not be
707   *                     {@code null}.
708   * @param  attributes  The set of attributes to include in the entry to add.
709   *                     It must not be {@code null}.
710   *
711   * @return  The result of processing the add operation.
712   *
713   * @throws  LDAPException  If the server rejects the add request, or if a
714   *                         problem is encountered while sending the request or
715   *                         reading the response.
716   */
717  @Override()
718  @NotNull()
719  public final LDAPResult add(@NotNull final String dn,
720                              @NotNull final Collection<Attribute> attributes)
721         throws LDAPException
722  {
723    return add(new AddRequest(dn, attributes));
724  }
725
726
727
728  /**
729   * Processes an add operation with the provided information using a connection
730   * from this connection pool.
731   *
732   * @param  entry  The entry to add.  It must not be {@code null}.
733   *
734   * @return  The result of processing the add operation.
735   *
736   * @throws  LDAPException  If the server rejects the add request, or if a
737   *                         problem is encountered while sending the request or
738   *                         reading the response.
739   */
740  @Override()
741  @NotNull()
742  public final LDAPResult add(@NotNull final Entry entry)
743         throws LDAPException
744  {
745    return add(new AddRequest(entry));
746  }
747
748
749
750  /**
751   * Processes an add operation with the provided information using a connection
752   * from this connection pool.
753   *
754   * @param  ldifLines  The lines that comprise an LDIF representation of the
755   *                    entry to add.  It must not be empty or {@code null}.
756   *
757   * @return  The result of processing the add operation.
758   *
759   * @throws  LDIFException  If the provided entry lines cannot be decoded as an
760   *                         entry in LDIF form.
761   *
762   * @throws  LDAPException  If the server rejects the add request, or if a
763   *                         problem is encountered while sending the request or
764   *                         reading the response.
765   */
766  @Override()
767  @NotNull()
768  public final LDAPResult add(@NotNull final String... ldifLines)
769         throws LDIFException, LDAPException
770  {
771    return add(new AddRequest(ldifLines));
772  }
773
774
775
776  /**
777   * Processes the provided add request using a connection from this connection
778   * pool.
779   *
780   * @param  addRequest  The add request to be processed.  It must not be
781   *                     {@code null}.
782   *
783   * @return  The result of processing the add operation.
784   *
785   * @throws  LDAPException  If the server rejects the add request, or if a
786   *                         problem is encountered while sending the request or
787   *                         reading the response.
788   */
789  @Override()
790  @NotNull()
791  public final LDAPResult add(@NotNull final AddRequest addRequest)
792         throws LDAPException
793  {
794    final LDAPConnection conn = getConnection();
795
796    try
797    {
798      final LDAPResult result = conn.add(addRequest);
799      releaseConnection(conn);
800      return result;
801    }
802    catch (final Throwable t)
803    {
804      throwLDAPExceptionIfShouldNotRetry(t, OperationType.ADD, conn);
805
806      // If we have gotten here, then we should retry the operation with a
807      // newly-created connection.
808      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
809
810      try
811      {
812        final LDAPResult result = newConn.add(addRequest);
813        releaseConnection(newConn);
814        return result;
815      }
816      catch (final Throwable t2)
817      {
818        throwLDAPException(t2, newConn);
819      }
820
821      // This return statement should never be reached.
822      return null;
823    }
824  }
825
826
827
828  /**
829   * Processes the provided add request using a connection from this connection
830   * pool.
831   *
832   * @param  addRequest  The add request to be processed.  It must not be
833   *                     {@code null}.
834   *
835   * @return  The result of processing the add operation.
836   *
837   * @throws  LDAPException  If the server rejects the add request, or if a
838   *                         problem is encountered while sending the request or
839   *                         reading the response.
840   */
841  @Override()
842  @NotNull()
843  public final LDAPResult add(@NotNull final ReadOnlyAddRequest addRequest)
844         throws LDAPException
845  {
846    return add((AddRequest) addRequest);
847  }
848
849
850
851  /**
852   * Processes a simple bind request with the provided DN and password using a
853   * connection from this connection pool.  Note that this will impact the state
854   * of the connection in the pool, and therefore this method should only be
855   * used if this connection pool is used exclusively for processing bind
856   * operations, or if the retain identity request control (a proprietary
857   * control for use with the Ping Identity, UnboundID, or Nokia/Alcatel-Lucent
858   * 8661 Directory Server) is included in the bind request to ensure that the
859   * authentication state is not impacted.
860   *
861   * @param  bindDN    The bind DN for the bind operation.
862   * @param  password  The password for the simple bind operation.
863   *
864   * @return  The result of processing the bind operation.
865   *
866   * @throws  LDAPException  If the server rejects the bind request, or if a
867   *                         problem occurs while sending the request or reading
868   *                         the response.
869   */
870  @NotNull()
871  public final BindResult bind(@Nullable final String bindDN,
872                               @Nullable final String password)
873         throws LDAPException
874  {
875    return bind(new SimpleBindRequest(bindDN, password));
876  }
877
878
879
880  /**
881   * Processes the provided bind request using a connection from this connection
882   * pool.  Note that this will impact the state of the connection in the pool,
883   * and therefore this method should only be used if this connection pool is
884   * used exclusively for processing bind operations, or if the retain identity
885   * request control (a proprietary control for use with the Ping Identity,
886   * UnboundID, or Nokia/Alcatel-Lucent 8661 Directory Server) is included in
887   * the bind request to ensure that the authentication state is not impacted.
888   *
889   * @param  bindRequest  The bind request to be processed.  It must not be
890   *                      {@code null}.
891   *
892   * @return  The result of processing the bind operation.
893   *
894   * @throws  LDAPException  If the server rejects the bind request, or if a
895   *                         problem occurs while sending the request or reading
896   *                         the response.
897   */
898  @NotNull
899  public final BindResult bind(@NotNull final BindRequest bindRequest)
900         throws LDAPException
901  {
902    final LDAPConnection conn = getConnection();
903
904    try
905    {
906      final BindResult result = conn.bind(bindRequest);
907      releaseConnection(conn);
908      return result;
909    }
910    catch (final Throwable t)
911    {
912      throwLDAPExceptionIfShouldNotRetry(t, OperationType.BIND, conn);
913
914      // If we have gotten here, then we should retry the operation with a
915      // newly-created connection.
916      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
917
918      try
919      {
920        final BindResult result = newConn.bind(bindRequest);
921        releaseConnection(newConn);
922        return result;
923      }
924      catch (final Throwable t2)
925      {
926        throwLDAPException(t2, newConn);
927      }
928
929      // This return statement should never be reached.
930      return null;
931    }
932  }
933
934
935
936  /**
937   * Processes a compare operation with the provided information using a
938   * connection from this connection pool.
939   *
940   * @param  dn              The DN of the entry in which to make the
941   *                         comparison.  It must not be {@code null}.
942   * @param  attributeName   The attribute name for which to make the
943   *                         comparison.  It must not be {@code null}.
944   * @param  assertionValue  The assertion value to verify in the target entry.
945   *                         It must not be {@code null}.
946   *
947   * @return  The result of processing the compare operation.
948   *
949   * @throws  LDAPException  If the server rejects the compare request, or if a
950   *                         problem is encountered while sending the request or
951   *                         reading the response.
952   */
953  @Override()
954  @NotNull()
955  public final CompareResult compare(@NotNull final String dn,
956                                     @NotNull final String attributeName,
957                                     @NotNull final String assertionValue)
958         throws LDAPException
959  {
960    return compare(new CompareRequest(dn, attributeName, assertionValue));
961  }
962
963
964
965  /**
966   * Processes the provided compare request using a connection from this
967   * connection pool.
968   *
969   * @param  compareRequest  The compare request to be processed.  It must not
970   *                         be {@code null}.
971   *
972   * @return  The result of processing the compare operation.
973   *
974   * @throws  LDAPException  If the server rejects the compare request, or if a
975   *                         problem is encountered while sending the request or
976   *                         reading the response.
977   */
978  @Override()
979  @NotNull()
980  public final CompareResult compare(
981                                  @NotNull final CompareRequest compareRequest)
982         throws LDAPException
983  {
984    final LDAPConnection conn = getConnection();
985
986    try
987    {
988      final CompareResult result = conn.compare(compareRequest);
989      releaseConnection(conn);
990      return result;
991    }
992    catch (final Throwable t)
993    {
994      throwLDAPExceptionIfShouldNotRetry(t, OperationType.COMPARE, conn);
995
996      // If we have gotten here, then we should retry the operation with a
997      // newly-created connection.
998      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
999
1000      try
1001      {
1002        final CompareResult result = newConn.compare(compareRequest);
1003        releaseConnection(newConn);
1004        return result;
1005      }
1006      catch (final Throwable t2)
1007      {
1008        throwLDAPException(t2, newConn);
1009      }
1010
1011      // This return statement should never be reached.
1012      return null;
1013    }
1014  }
1015
1016
1017
1018  /**
1019   * Processes the provided compare request using a connection from this
1020   * connection pool.
1021   *
1022   * @param  compareRequest  The compare request to be processed.  It must not
1023   *                         be {@code null}.
1024   *
1025   * @return  The result of processing the compare operation.
1026   *
1027   * @throws  LDAPException  If the server rejects the compare request, or if a
1028   *                         problem is encountered while sending the request or
1029   *                         reading the response.
1030   */
1031  @Override()
1032  @NotNull()
1033  public final CompareResult compare(
1034                    @NotNull final ReadOnlyCompareRequest compareRequest)
1035         throws LDAPException
1036  {
1037    return compare((CompareRequest) compareRequest);
1038  }
1039
1040
1041
1042  /**
1043   * Deletes the entry with the specified DN using a connection from this
1044   * connection pool.
1045   *
1046   * @param  dn  The DN of the entry to delete.  It must not be {@code null}.
1047   *
1048   * @return  The result of processing the delete operation.
1049   *
1050   * @throws  LDAPException  If the server rejects the delete request, or if a
1051   *                         problem is encountered while sending the request or
1052   *                         reading the response.
1053   */
1054  @Override()
1055  @NotNull()
1056  public final LDAPResult delete(@NotNull final String dn)
1057         throws LDAPException
1058  {
1059    return delete(new DeleteRequest(dn));
1060  }
1061
1062
1063
1064  /**
1065   * Processes the provided delete request using a connection from this
1066   * connection pool.
1067   *
1068   * @param  deleteRequest  The delete request to be processed.  It must not be
1069   *                        {@code null}.
1070   *
1071   * @return  The result of processing the delete operation.
1072   *
1073   * @throws  LDAPException  If the server rejects the delete request, or if a
1074   *                         problem is encountered while sending the request or
1075   *                         reading the response.
1076   */
1077  @Override()
1078  @NotNull()
1079  public final LDAPResult delete(@NotNull final DeleteRequest deleteRequest)
1080         throws LDAPException
1081  {
1082    final LDAPConnection conn = getConnection();
1083
1084    try
1085    {
1086      final LDAPResult result = conn.delete(deleteRequest);
1087      releaseConnection(conn);
1088      return result;
1089    }
1090    catch (final Throwable t)
1091    {
1092      throwLDAPExceptionIfShouldNotRetry(t, OperationType.DELETE, conn);
1093
1094      // If we have gotten here, then we should retry the operation with a
1095      // newly-created connection.
1096      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
1097
1098      try
1099      {
1100        final LDAPResult result = newConn.delete(deleteRequest);
1101        releaseConnection(newConn);
1102        return result;
1103      }
1104      catch (final Throwable t2)
1105      {
1106        throwLDAPException(t2, newConn);
1107      }
1108
1109      // This return statement should never be reached.
1110      return null;
1111    }
1112  }
1113
1114
1115
1116  /**
1117   * Processes the provided delete request using a connection from this
1118   * connection pool.
1119   *
1120   * @param  deleteRequest  The delete request to be processed.  It must not be
1121   *                        {@code null}.
1122   *
1123   * @return  The result of processing the delete operation.
1124   *
1125   * @throws  LDAPException  If the server rejects the delete request, or if a
1126   *                         problem is encountered while sending the request or
1127   *                         reading the response.
1128   */
1129  @Override()
1130  @NotNull()
1131  public final LDAPResult delete(
1132                    @NotNull final ReadOnlyDeleteRequest deleteRequest)
1133         throws LDAPException
1134  {
1135    return delete((DeleteRequest) deleteRequest);
1136  }
1137
1138
1139
1140  /**
1141   * Processes an extended operation with the provided request OID using a
1142   * connection from this connection pool.  Note that this method should not be
1143   * used to perform any operation that will alter the state of the connection
1144   * in the pool (e.g., a StartTLS operation) or that involves multiple
1145   * distinct operations on the same connection (e.g., LDAP transactions).
1146   *
1147   * @param  requestOID  The OID for the extended request to process.  It must
1148   *                     not be {@code null}.
1149   *
1150   * @return  The extended result object that provides information about the
1151   *          result of the request processing.
1152   *
1153   * @throws  LDAPException  If a problem occurs while sending the request or
1154   *                         reading the response.
1155   */
1156  @NotNull()
1157  public final ExtendedResult processExtendedOperation(
1158                                   @NotNull final String requestOID)
1159         throws LDAPException
1160  {
1161    return processExtendedOperation(new ExtendedRequest(requestOID));
1162  }
1163
1164
1165
1166  /**
1167   * Processes an extended operation with the provided request OID and value
1168   * using a connection from this connection pool.  Note that this method should
1169   * not be used to perform any operation that will alter the state of the
1170   * connection in the pool (e.g., a StartTLS operation) or that involves
1171   * multiple distinct operations on the same connection (e.g., LDAP
1172   * transactions).
1173   *
1174   * @param  requestOID    The OID for the extended request to process.  It must
1175   *                       not be {@code null}.
1176   * @param  requestValue  The encoded value for the extended request to
1177   *                       process.  It may be {@code null} if there does not
1178   *                       need to be a value for the requested operation.
1179   *
1180   * @return  The extended result object that provides information about the
1181   *          result of the request processing.
1182   *
1183   * @throws  LDAPException  If a problem occurs while sending the request or
1184   *                         reading the response.
1185   */
1186  @NotNull()
1187  public final ExtendedResult processExtendedOperation(
1188                                   @NotNull final String requestOID,
1189                                   @Nullable final ASN1OctetString requestValue)
1190         throws LDAPException
1191  {
1192    return processExtendedOperation(new ExtendedRequest(requestOID,
1193         requestValue));
1194  }
1195
1196
1197
1198  /**
1199   * Processes the provided extended request using a connection from this
1200   * connection pool.  Note that this method should not be used to perform any
1201   * operation that will alter the state of the connection in the pool (e.g., a
1202   * StartTLS operation) or that involves multiple distinct operations on the
1203   * same connection (e.g., LDAP transactions).
1204   *
1205   * @param  extendedRequest  The extended request to be processed.  It must not
1206   *                          be {@code null}.
1207   *
1208   * @return  The extended result object that provides information about the
1209   *          result of the request processing.
1210   *
1211   * @throws  LDAPException  If a problem occurs while sending the request or
1212   *                         reading the response.
1213   */
1214  @NotNull()
1215  public final ExtendedResult processExtendedOperation(
1216                    @NotNull final ExtendedRequest extendedRequest)
1217         throws LDAPException
1218  {
1219    if (extendedRequest.getOID().equals(
1220         StartTLSExtendedRequest.STARTTLS_REQUEST_OID))
1221    {
1222      throw new LDAPException(ResultCode.NOT_SUPPORTED,
1223                              ERR_POOL_STARTTLS_NOT_ALLOWED.get());
1224    }
1225
1226    final LDAPConnection conn = getConnection();
1227
1228    try
1229    {
1230      final ExtendedResult result =
1231           conn.processExtendedOperation(extendedRequest);
1232      releaseConnection(conn);
1233      return result;
1234    }
1235    catch (final Throwable t)
1236    {
1237      throwLDAPExceptionIfShouldNotRetry(t, OperationType.EXTENDED, conn);
1238
1239      // If we have gotten here, then we should retry the operation with a
1240      // newly-created connection.
1241      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
1242
1243      try
1244      {
1245        final ExtendedResult result =
1246             newConn.processExtendedOperation(extendedRequest);
1247        releaseConnection(newConn);
1248        return result;
1249      }
1250      catch (final Throwable t2)
1251      {
1252        throwLDAPException(t2, newConn);
1253      }
1254
1255      // This return statement should never be reached.
1256      return null;
1257    }
1258  }
1259
1260
1261
1262  /**
1263   * Applies the provided modification to the specified entry using a connection
1264   * from this connection pool.
1265   *
1266   * @param  dn   The DN of the entry to modify.  It must not be {@code null}.
1267   * @param  mod  The modification to apply to the target entry.  It must not
1268   *              be {@code null}.
1269   *
1270   * @return  The result of processing the modify operation.
1271   *
1272   * @throws  LDAPException  If the server rejects the modify request, or if a
1273   *                         problem is encountered while sending the request or
1274   *                         reading the response.
1275   */
1276  @Override()
1277  @NotNull()
1278  public final LDAPResult modify(@NotNull final String dn,
1279                                 @NotNull final Modification mod)
1280         throws LDAPException
1281  {
1282    return modify(new ModifyRequest(dn, mod));
1283  }
1284
1285
1286
1287  /**
1288   * Applies the provided set of modifications to the specified entry using a
1289   * connection from this connection pool.
1290   *
1291   * @param  dn    The DN of the entry to modify.  It must not be {@code null}.
1292   * @param  mods  The set of modifications to apply to the target entry.  It
1293   *               must not be {@code null} or empty.  *
1294   * @return  The result of processing the modify operation.
1295   *
1296   * @throws  LDAPException  If the server rejects the modify request, or if a
1297   *                         problem is encountered while sending the request or
1298   *                         reading the response.
1299   */
1300  @Override()
1301  @NotNull()
1302  public final LDAPResult modify(@NotNull final String dn,
1303                                 @NotNull final Modification... mods)
1304         throws LDAPException
1305  {
1306    return modify(new ModifyRequest(dn, mods));
1307  }
1308
1309
1310
1311  /**
1312   * Applies the provided set of modifications to the specified entry using a
1313   * connection from this connection pool.
1314   *
1315   * @param  dn    The DN of the entry to modify.  It must not be {@code null}.
1316   * @param  mods  The set of modifications to apply to the target entry.  It
1317   *               must not be {@code null} or empty.
1318   *
1319   * @return  The result of processing the modify operation.
1320   *
1321   * @throws  LDAPException  If the server rejects the modify request, or if a
1322   *                         problem is encountered while sending the request or
1323   *                         reading the response.
1324   */
1325  @Override()
1326  @NotNull()
1327  public final LDAPResult modify(@NotNull final String dn,
1328                                 @NotNull final List<Modification> mods)
1329         throws LDAPException
1330  {
1331    return modify(new ModifyRequest(dn, mods));
1332  }
1333
1334
1335
1336  /**
1337   * Processes a modify request from the provided LDIF representation of the
1338   * changes using a connection from this connection pool.
1339   *
1340   * @param  ldifModificationLines  The lines that comprise an LDIF
1341   *                                representation of a modify change record.
1342   *                                It must not be {@code null} or empty.
1343   *
1344   * @return  The result of processing the modify operation.
1345   *
1346   * @throws  LDIFException  If the provided set of lines cannot be parsed as an
1347   *                         LDIF modify change record.
1348   *
1349   * @throws  LDAPException  If the server rejects the modify request, or if a
1350   *                         problem is encountered while sending the request or
1351   *                         reading the response.
1352   *
1353   */
1354  @Override()
1355  @NotNull()
1356  public final LDAPResult modify(@NotNull final String... ldifModificationLines)
1357         throws LDIFException, LDAPException
1358  {
1359    return modify(new ModifyRequest(ldifModificationLines));
1360  }
1361
1362
1363
1364  /**
1365   * Processes the provided modify request using a connection from this
1366   * connection pool.
1367   *
1368   * @param  modifyRequest  The modify request to be processed.  It must not be
1369   *                        {@code null}.
1370   *
1371   * @return  The result of processing the modify operation.
1372   *
1373   * @throws  LDAPException  If the server rejects the modify request, or if a
1374   *                         problem is encountered while sending the request or
1375   *                         reading the response.
1376   */
1377  @Override()
1378  @NotNull()
1379  public final LDAPResult modify(@NotNull final ModifyRequest modifyRequest)
1380         throws LDAPException
1381  {
1382    final LDAPConnection conn = getConnection();
1383
1384    try
1385    {
1386      final LDAPResult result = conn.modify(modifyRequest);
1387      releaseConnection(conn);
1388      return result;
1389    }
1390    catch (final Throwable t)
1391    {
1392      throwLDAPExceptionIfShouldNotRetry(t, OperationType.MODIFY, conn);
1393
1394      // If we have gotten here, then we should retry the operation with a
1395      // newly-created connection.
1396      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
1397
1398      try
1399      {
1400        final LDAPResult result = newConn.modify(modifyRequest);
1401        releaseConnection(newConn);
1402        return result;
1403      }
1404      catch (final Throwable t2)
1405      {
1406        throwLDAPException(t2, newConn);
1407      }
1408
1409      // This return statement should never be reached.
1410      return null;
1411    }
1412  }
1413
1414
1415
1416  /**
1417   * Processes the provided modify request using a connection from this
1418   * connection pool.
1419   *
1420   * @param  modifyRequest  The modify request to be processed.  It must not be
1421   *                        {@code null}.
1422   *
1423   * @return  The result of processing the modify operation.
1424   *
1425   * @throws  LDAPException  If the server rejects the modify request, or if a
1426   *                         problem is encountered while sending the request or
1427   *                         reading the response.
1428   */
1429  @Override()
1430  @NotNull()
1431  public final LDAPResult modify(
1432                    @NotNull final ReadOnlyModifyRequest modifyRequest)
1433         throws LDAPException
1434  {
1435    return modify((ModifyRequest) modifyRequest);
1436  }
1437
1438
1439
1440  /**
1441   * Performs a modify DN operation with the provided information using a
1442   * connection from this connection pool.
1443   *
1444   * @param  dn            The current DN for the entry to rename.  It must not
1445   *                       be {@code null}.
1446   * @param  newRDN        The new RDN to use for the entry.  It must not be
1447   *                       {@code null}.
1448   * @param  deleteOldRDN  Indicates whether to delete the current RDN value
1449   *                       from the entry.
1450   *
1451   * @return  The result of processing the modify DN operation.
1452   *
1453   * @throws  LDAPException  If the server rejects the modify DN request, or if
1454   *                         a problem is encountered while sending the request
1455   *                         or reading the response.
1456   */
1457  @Override()
1458  @NotNull()
1459  public final LDAPResult modifyDN(@NotNull final String dn,
1460                                   @NotNull final String newRDN,
1461                                   final boolean deleteOldRDN)
1462         throws LDAPException
1463  {
1464    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN));
1465  }
1466
1467
1468
1469  /**
1470   * Performs a modify DN operation with the provided information using a
1471   * connection from this connection pool.
1472   *
1473   * @param  dn             The current DN for the entry to rename.  It must not
1474   *                        be {@code null}.
1475   * @param  newRDN         The new RDN to use for the entry.  It must not be
1476   *                        {@code null}.
1477   * @param  deleteOldRDN   Indicates whether to delete the current RDN value
1478   *                        from the entry.
1479   * @param  newSuperiorDN  The new superior DN for the entry.  It may be
1480   *                        {@code null} if the entry is not to be moved below a
1481   *                        new parent.
1482   *
1483   * @return  The result of processing the modify DN operation.
1484   *
1485   * @throws  LDAPException  If the server rejects the modify DN request, or if
1486   *                         a problem is encountered while sending the request
1487   *                         or reading the response.
1488   */
1489  @Override()
1490  @NotNull()
1491  public final LDAPResult modifyDN(@NotNull final String dn,
1492                                   @NotNull final String newRDN,
1493                                   final boolean deleteOldRDN,
1494                                   @Nullable final String newSuperiorDN)
1495         throws LDAPException
1496  {
1497    return modifyDN(new ModifyDNRequest(dn, newRDN, deleteOldRDN,
1498         newSuperiorDN));
1499  }
1500
1501
1502
1503  /**
1504   * Processes the provided modify DN request using a connection from this
1505   * connection pool.
1506   *
1507   * @param  modifyDNRequest  The modify DN request to be processed.  It must
1508   *                          not be {@code null}.
1509   *
1510   * @return  The result of processing the modify DN operation.
1511   *
1512   * @throws  LDAPException  If the server rejects the modify DN request, or if
1513   *                         a problem is encountered while sending the request
1514   *                         or reading the response.
1515   */
1516  @Override()
1517  @NotNull()
1518  public final LDAPResult modifyDN(
1519                    @NotNull final ModifyDNRequest modifyDNRequest)
1520         throws LDAPException
1521  {
1522    final LDAPConnection conn = getConnection();
1523
1524    try
1525    {
1526      final LDAPResult result = conn.modifyDN(modifyDNRequest);
1527      releaseConnection(conn);
1528      return result;
1529    }
1530    catch (final Throwable t)
1531    {
1532      throwLDAPExceptionIfShouldNotRetry(t, OperationType.MODIFY_DN, conn);
1533
1534      // If we have gotten here, then we should retry the operation with a
1535      // newly-created connection.
1536      final LDAPConnection newConn = replaceDefunctConnection(t, conn);
1537
1538      try
1539      {
1540        final LDAPResult result = newConn.modifyDN(modifyDNRequest);
1541        releaseConnection(newConn);
1542        return result;
1543      }
1544      catch (final Throwable t2)
1545      {
1546        throwLDAPException(t2, newConn);
1547      }
1548
1549      // This return statement should never be reached.
1550      return null;
1551    }
1552  }
1553
1554
1555
1556  /**
1557   * Processes the provided modify DN request using a connection from this
1558   * connection pool.
1559   *
1560   * @param  modifyDNRequest  The modify DN request to be processed.  It must
1561   *                          not be {@code null}.
1562   *
1563   * @return  The result of processing the modify DN operation.
1564   *
1565   * @throws  LDAPException  If the server rejects the modify DN request, or if
1566   *                         a problem is encountered while sending the request
1567   *                         or reading the response.
1568   */
1569  @Override()
1570  @NotNull()
1571  public final LDAPResult modifyDN(
1572                    @NotNull final ReadOnlyModifyDNRequest modifyDNRequest)
1573         throws LDAPException
1574  {
1575    return modifyDN((ModifyDNRequest) modifyDNRequest);
1576  }
1577
1578
1579
1580  /**
1581   * Processes a search operation with the provided information using a
1582   * connection from this connection pool.  The search result entries and
1583   * references will be collected internally and included in the
1584   * {@code SearchResult} object that is returned.
1585   * <BR><BR>
1586   * Note that if the search does not complete successfully, an
1587   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1588   * search result entries or references may have been returned before the
1589   * failure response is received.  In this case, the
1590   * {@code LDAPSearchException} methods like {@code getEntryCount},
1591   * {@code getSearchEntries}, {@code getReferenceCount}, and
1592   * {@code getSearchReferences} may be used to obtain information about those
1593   * entries and references.
1594   *
1595   * @param  baseDN      The base DN for the search request.  It must not be
1596   *                     {@code null}.
1597   * @param  scope       The scope that specifies the range of entries that
1598   *                     should be examined for the search.
1599   * @param  filter      The string representation of the filter to use to
1600   *                     identify matching entries.  It must not be
1601   *                     {@code null}.
1602   * @param  attributes  The set of attributes that should be returned in
1603   *                     matching entries.  It may be {@code null} or empty if
1604   *                     the default attribute set (all user attributes) is to
1605   *                     be requested.
1606   *
1607   * @return  A search result object that provides information about the
1608   *          processing of the search, including the set of matching entries
1609   *          and search references returned by the server.
1610   *
1611   * @throws  LDAPSearchException  If the search does not complete successfully,
1612   *                               or if a problem is encountered while parsing
1613   *                               the provided filter string, sending the
1614   *                               request, or reading the response.  If one or
1615   *                               more entries or references were returned
1616   *                               before the failure was encountered, then the
1617   *                               {@code LDAPSearchException} object may be
1618   *                               examined to obtain information about those
1619   *                               entries and/or references.
1620   */
1621  @Override()
1622  @NotNull()
1623  public final SearchResult search(@NotNull final String baseDN,
1624                                   @NotNull final SearchScope scope,
1625                                   @NotNull final String filter,
1626                                   @Nullable final String... attributes)
1627         throws LDAPSearchException
1628  {
1629    return search(new SearchRequest(baseDN, scope, parseFilter(filter),
1630         attributes));
1631  }
1632
1633
1634
1635  /**
1636   * Processes a search operation with the provided information using a
1637   * connection from this connection pool.  The search result entries and
1638   * references will be collected internally and included in the
1639   * {@code SearchResult} object that is returned.
1640   * <BR><BR>
1641   * Note that if the search does not complete successfully, an
1642   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1643   * search result entries or references may have been returned before the
1644   * failure response is received.  In this case, the
1645   * {@code LDAPSearchException} methods like {@code getEntryCount},
1646   * {@code getSearchEntries}, {@code getReferenceCount}, and
1647   * {@code getSearchReferences} may be used to obtain information about those
1648   * entries and references.
1649   *
1650   * @param  baseDN      The base DN for the search request.  It must not be
1651   *                     {@code null}.
1652   * @param  scope       The scope that specifies the range of entries that
1653   *                     should be examined for the search.
1654   * @param  filter      The filter to use to identify matching entries.  It
1655   *                     must not be {@code null}.
1656   * @param  attributes  The set of attributes that should be returned in
1657   *                     matching entries.  It may be {@code null} or empty if
1658   *                     the default attribute set (all user attributes) is to
1659   *                     be requested.
1660   *
1661   * @return  A search result object that provides information about the
1662   *          processing of the search, including the set of matching entries
1663   *          and search references returned by the server.
1664   *
1665   * @throws  LDAPSearchException  If the search does not complete successfully,
1666   *                               or if a problem is encountered while sending
1667   *                               the request or reading the response.  If one
1668   *                               or more entries or references were returned
1669   *                               before the failure was encountered, then the
1670   *                               {@code LDAPSearchException} object may be
1671   *                               examined to obtain information about those
1672   *                               entries and/or references.
1673   */
1674  @Override()
1675  @NotNull()
1676  public final SearchResult search(@NotNull final String baseDN,
1677                                   @NotNull final SearchScope scope,
1678                                   @NotNull final Filter filter,
1679                                   @Nullable final String... attributes)
1680         throws LDAPSearchException
1681  {
1682    return search(new SearchRequest(baseDN, scope, filter, attributes));
1683  }
1684
1685
1686
1687  /**
1688   * Processes a search operation with the provided information using a
1689   * connection from this connection pool.
1690   * <BR><BR>
1691   * Note that if the search does not complete successfully, an
1692   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1693   * search result entries or references may have been returned before the
1694   * failure response is received.  In this case, the
1695   * {@code LDAPSearchException} methods like {@code getEntryCount},
1696   * {@code getSearchEntries}, {@code getReferenceCount}, and
1697   * {@code getSearchReferences} may be used to obtain information about those
1698   * entries and references (although if a search result listener was provided,
1699   * then it will have been used to make any entries and references available,
1700   * and they will not be available through the {@code getSearchEntries} and
1701   * {@code getSearchReferences} methods).
1702   *
1703   * @param  searchResultListener  The search result listener that should be
1704   *                               used to return results to the client.  It may
1705   *                               be {@code null} if the search results should
1706   *                               be collected internally and returned in the
1707   *                               {@code SearchResult} object.
1708   * @param  baseDN                The base DN for the search request.  It must
1709   *                               not be {@code null}.
1710   * @param  scope                 The scope that specifies the range of entries
1711   *                               that should be examined for the search.
1712   * @param  filter                The string representation of the filter to
1713   *                               use to identify matching entries.  It must
1714   *                               not be {@code null}.
1715   * @param  attributes            The set of attributes that should be returned
1716   *                               in matching entries.  It may be {@code null}
1717   *                               or empty if the default attribute set (all
1718   *                               user attributes) is to be requested.
1719   *
1720   * @return  A search result object that provides information about the
1721   *          processing of the search, potentially including the set of
1722   *          matching entries and search references returned by the server.
1723   *
1724   * @throws  LDAPSearchException  If the search does not complete successfully,
1725   *                               or if a problem is encountered while parsing
1726   *                               the provided filter string, sending the
1727   *                               request, or reading the response.  If one
1728   *                               or more entries or references were returned
1729   *                               before the failure was encountered, then the
1730   *                               {@code LDAPSearchException} object may be
1731   *                               examined to obtain information about those
1732   *                               entries and/or references.
1733   */
1734  @Override()
1735  @NotNull()
1736  public final SearchResult search(
1737       @Nullable final SearchResultListener searchResultListener,
1738       @NotNull final String baseDN, @NotNull final SearchScope scope,
1739       @NotNull final String filter, @Nullable final String... attributes)
1740         throws LDAPSearchException
1741  {
1742    return search(new SearchRequest(searchResultListener, baseDN, scope,
1743         parseFilter(filter), attributes));
1744  }
1745
1746
1747
1748  /**
1749   * Processes a search operation with the provided information using a
1750   * connection from this connection pool.
1751   * <BR><BR>
1752   * Note that if the search does not complete successfully, an
1753   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1754   * search result entries or references may have been returned before the
1755   * failure response is received.  In this case, the
1756   * {@code LDAPSearchException} methods like {@code getEntryCount},
1757   * {@code getSearchEntries}, {@code getReferenceCount}, and
1758   * {@code getSearchReferences} may be used to obtain information about those
1759   * entries and references (although if a search result listener was provided,
1760   * then it will have been used to make any entries and references available,
1761   * and they will not be available through the {@code getSearchEntries} and
1762   * {@code getSearchReferences} methods).
1763   *
1764   * @param  searchResultListener  The search result listener that should be
1765   *                               used to return results to the client.  It may
1766   *                               be {@code null} if the search results should
1767   *                               be collected internally and returned in the
1768   *                               {@code SearchResult} object.
1769   * @param  baseDN                The base DN for the search request.  It must
1770   *                               not be {@code null}.
1771   * @param  scope                 The scope that specifies the range of entries
1772   *                               that should be examined for the search.
1773   * @param  filter                The filter to use to identify matching
1774   *                               entries.  It must not be {@code null}.
1775   * @param  attributes            The set of attributes that should be returned
1776   *                               in matching entries.  It may be {@code null}
1777   *                               or empty if the default attribute set (all
1778   *                               user attributes) is to be requested.
1779   *
1780   * @return  A search result object that provides information about the
1781   *          processing of the search, potentially including the set of
1782   *          matching entries and search references returned by the server.
1783   *
1784   * @throws  LDAPSearchException  If the search does not complete successfully,
1785   *                               or if a problem is encountered while sending
1786   *                               the request or reading the response.  If one
1787   *                               or more entries or references were returned
1788   *                               before the failure was encountered, then the
1789   *                               {@code LDAPSearchException} object may be
1790   *                               examined to obtain information about those
1791   *                               entries and/or references.
1792   */
1793  @Override()
1794  @NotNull()
1795  public final SearchResult search(
1796       @Nullable final SearchResultListener searchResultListener,
1797       @NotNull final String baseDN, @NotNull final SearchScope scope,
1798       @NotNull final Filter filter, @Nullable final String... attributes)
1799         throws LDAPSearchException
1800  {
1801    return search(new SearchRequest(searchResultListener, baseDN, scope,
1802         filter, attributes));
1803  }
1804
1805
1806
1807  /**
1808   * Processes a search operation with the provided information using a
1809   * connection from this connection pool.  The search result entries and
1810   * references will be collected internally and included in the
1811   * {@code SearchResult} object that is returned.
1812   * <BR><BR>
1813   * Note that if the search does not complete successfully, an
1814   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1815   * search result entries or references may have been returned before the
1816   * failure response is received.  In this case, the
1817   * {@code LDAPSearchException} methods like {@code getEntryCount},
1818   * {@code getSearchEntries}, {@code getReferenceCount}, and
1819   * {@code getSearchReferences} may be used to obtain information about those
1820   * entries and references.
1821   *
1822   * @param  baseDN       The base DN for the search request.  It must not be
1823   *                      {@code null}.
1824   * @param  scope        The scope that specifies the range of entries that
1825   *                      should be examined for the search.
1826   * @param  derefPolicy  The dereference policy the server should use for any
1827   *                      aliases encountered while processing the search.
1828   * @param  sizeLimit    The maximum number of entries that the server should
1829   *                      return for the search.  A value of zero indicates that
1830   *                      there should be no limit.
1831   * @param  timeLimit    The maximum length of time in seconds that the server
1832   *                      should spend processing this search request.  A value
1833   *                      of zero indicates that there should be no limit.
1834   * @param  typesOnly    Indicates whether to return only attribute names in
1835   *                      matching entries, or both attribute names and values.
1836   * @param  filter       The string representation of the filter to use to
1837   *                      identify matching entries.  It must not be
1838   *                      {@code null}.
1839   * @param  attributes   The set of attributes that should be returned in
1840   *                      matching entries.  It may be {@code null} or empty if
1841   *                      the default attribute set (all user attributes) is to
1842   *                      be requested.
1843   *
1844   * @return  A search result object that provides information about the
1845   *          processing of the search, including the set of matching entries
1846   *          and search references returned by the server.
1847   *
1848   * @throws  LDAPSearchException  If the search does not complete successfully,
1849   *                               or if a problem is encountered while parsing
1850   *                               the provided filter string, sending the
1851   *                               request, or reading the response.  If one
1852   *                               or more entries or references were returned
1853   *                               before the failure was encountered, then the
1854   *                               {@code LDAPSearchException} object may be
1855   *                               examined to obtain information about those
1856   *                               entries and/or references.
1857   */
1858  @Override()
1859  @NotNull()
1860  public final SearchResult search(@NotNull final String baseDN,
1861                                   @NotNull final SearchScope scope,
1862                                   @NotNull final DereferencePolicy derefPolicy,
1863                                   final int sizeLimit, final int timeLimit,
1864                                   final boolean typesOnly,
1865                                   @NotNull final String filter,
1866                                   @Nullable final String... attributes)
1867         throws LDAPSearchException
1868  {
1869    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
1870         timeLimit, typesOnly, parseFilter(filter), attributes));
1871  }
1872
1873
1874
1875  /**
1876   * Processes a search operation with the provided information using a
1877   * connection from this connection pool.  The search result entries and
1878   * references will be collected internally and included in the
1879   * {@code SearchResult} object that is returned.
1880   * <BR><BR>
1881   * Note that if the search does not complete successfully, an
1882   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1883   * search result entries or references may have been returned before the
1884   * failure response is received.  In this case, the
1885   * {@code LDAPSearchException} methods like {@code getEntryCount},
1886   * {@code getSearchEntries}, {@code getReferenceCount}, and
1887   * {@code getSearchReferences} may be used to obtain information about those
1888   * entries and references.
1889   *
1890   * @param  baseDN       The base DN for the search request.  It must not be
1891   *                      {@code null}.
1892   * @param  scope        The scope that specifies the range of entries that
1893   *                      should be examined for the search.
1894   * @param  derefPolicy  The dereference policy the server should use for any
1895   *                      aliases encountered while processing the search.
1896   * @param  sizeLimit    The maximum number of entries that the server should
1897   *                      return for the search.  A value of zero indicates that
1898   *                      there should be no limit.
1899   * @param  timeLimit    The maximum length of time in seconds that the server
1900   *                      should spend processing this search request.  A value
1901   *                      of zero indicates that there should be no limit.
1902   * @param  typesOnly    Indicates whether to return only attribute names in
1903   *                      matching entries, or both attribute names and values.
1904   * @param  filter       The filter to use to identify matching entries.  It
1905   *                      must not be {@code null}.
1906   * @param  attributes   The set of attributes that should be returned in
1907   *                      matching entries.  It may be {@code null} or empty if
1908   *                      the default attribute set (all user attributes) is to
1909   *                      be requested.
1910   *
1911   * @return  A search result object that provides information about the
1912   *          processing of the search, including the set of matching entries
1913   *          and search references returned by the server.
1914   *
1915   * @throws  LDAPSearchException  If the search does not complete successfully,
1916   *                               or if a problem is encountered while sending
1917   *                               the request or reading the response.  If one
1918   *                               or more entries or references were returned
1919   *                               before the failure was encountered, then the
1920   *                               {@code LDAPSearchException} object may be
1921   *                               examined to obtain information about those
1922   *                               entries and/or references.
1923   */
1924  @Override()
1925  @NotNull()
1926  public final SearchResult search(@NotNull final String baseDN,
1927                                   @NotNull final SearchScope scope,
1928                                   @NotNull final DereferencePolicy derefPolicy,
1929                                   final int sizeLimit, final int timeLimit,
1930                                   final boolean typesOnly,
1931                                   @NotNull final Filter filter,
1932                                   @Nullable final String... attributes)
1933         throws LDAPSearchException
1934  {
1935    return search(new SearchRequest(baseDN, scope, derefPolicy, sizeLimit,
1936         timeLimit, typesOnly, filter, attributes));
1937  }
1938
1939
1940
1941  /**
1942   * Processes a search operation with the provided information using a
1943   * connection from this connection pool.
1944   * <BR><BR>
1945   * Note that if the search does not complete successfully, an
1946   * {@code LDAPSearchException} will be thrown  In some cases, one or more
1947   * search result entries or references may have been returned before the
1948   * failure response is received.  In this case, the
1949   * {@code LDAPSearchException} methods like {@code getEntryCount},
1950   * {@code getSearchEntries}, {@code getReferenceCount}, and
1951   * {@code getSearchReferences} may be used to obtain information about those
1952   * entries and references (although if a search result listener was provided,
1953   * then it will have been used to make any entries and references available,
1954   * and they will not be available through the {@code getSearchEntries} and
1955   * {@code getSearchReferences} methods).
1956   *
1957   * @param  searchResultListener  The search result listener that should be
1958   *                               used to return results to the client.  It may
1959   *                               be {@code null} if the search results should
1960   *                               be collected internally and returned in the
1961   *                               {@code SearchResult} object.
1962   * @param  baseDN                The base DN for the search request.  It must
1963   *                               not be {@code null}.
1964   * @param  scope                 The scope that specifies the range of entries
1965   *                               that should be examined for the search.
1966   * @param  derefPolicy           The dereference policy the server should use
1967   *                               for any aliases encountered while processing
1968   *                               the search.
1969   * @param  sizeLimit             The maximum number of entries that the server
1970   *                               should return for the search.  A value of
1971   *                               zero indicates that there should be no limit.
1972   * @param  timeLimit             The maximum length of time in seconds that
1973   *                               the server should spend processing this
1974   *                               search request.  A value of zero indicates
1975   *                               that there should be no limit.
1976   * @param  typesOnly             Indicates whether to return only attribute
1977   *                               names in matching entries, or both attribute
1978   *                               names and values.
1979   * @param  filter                The string representation of the filter to
1980   *                               use to identify matching entries.  It must
1981   *                               not be {@code null}.
1982   * @param  attributes            The set of attributes that should be returned
1983   *                               in matching entries.  It may be {@code null}
1984   *                               or empty if the default attribute set (all
1985   *                               user attributes) is to be requested.
1986   *
1987   * @return  A search result object that provides information about the
1988   *          processing of the search, potentially including the set of
1989   *          matching entries and search references returned by the server.
1990   *
1991   * @throws  LDAPSearchException  If the search does not complete successfully,
1992   *                               or if a problem is encountered while parsing
1993   *                               the provided filter string, sending the
1994   *                               request, or reading the response.  If one
1995   *                               or more entries or references were returned
1996   *                               before the failure was encountered, then the
1997   *                               {@code LDAPSearchException} object may be
1998   *                               examined to obtain information about those
1999   *                               entries and/or references.
2000   */
2001  @Override()
2002  @NotNull()
2003  public final SearchResult search(
2004       @Nullable final SearchResultListener searchResultListener,
2005       @NotNull final String baseDN, @NotNull final SearchScope scope,
2006       @NotNull final DereferencePolicy derefPolicy, final int sizeLimit,
2007       final int timeLimit, final boolean typesOnly,
2008       @NotNull final String filter, @Nullable final String... attributes)
2009         throws LDAPSearchException
2010  {
2011    return search(new SearchRequest(searchResultListener, baseDN, scope,
2012         derefPolicy, sizeLimit, timeLimit, typesOnly, parseFilter(filter),
2013         attributes));
2014  }
2015
2016
2017
2018  /**
2019   * Processes a search operation with the provided information using a
2020   * connection from this connection pool.
2021   * <BR><BR>
2022   * Note that if the search does not complete successfully, an
2023   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2024   * search result entries or references may have been returned before the
2025   * failure response is received.  In this case, the
2026   * {@code LDAPSearchException} methods like {@code getEntryCount},
2027   * {@code getSearchEntries}, {@code getReferenceCount}, and
2028   * {@code getSearchReferences} may be used to obtain information about those
2029   * entries and references (although if a search result listener was provided,
2030   * then it will have been used to make any entries and references available,
2031   * and they will not be available through the {@code getSearchEntries} and
2032   * {@code getSearchReferences} methods).
2033   *
2034   * @param  searchResultListener  The search result listener that should be
2035   *                               used to return results to the client.  It may
2036   *                               be {@code null} if the search results should
2037   *                               be collected internally and returned in the
2038   *                               {@code SearchResult} object.
2039   * @param  baseDN                The base DN for the search request.  It must
2040   *                               not be {@code null}.
2041   * @param  scope                 The scope that specifies the range of entries
2042   *                               that should be examined for the search.
2043   * @param  derefPolicy           The dereference policy the server should use
2044   *                               for any aliases encountered while processing
2045   *                               the search.
2046   * @param  sizeLimit             The maximum number of entries that the server
2047   *                               should return for the search.  A value of
2048   *                               zero indicates that there should be no limit.
2049   * @param  timeLimit             The maximum length of time in seconds that
2050   *                               the server should spend processing this
2051   *                               search request.  A value of zero indicates
2052   *                               that there should be no limit.
2053   * @param  typesOnly             Indicates whether to return only attribute
2054   *                               names in matching entries, or both attribute
2055   *                               names and values.
2056   * @param  filter                The filter to use to identify matching
2057   *                               entries.  It must not be {@code null}.
2058   * @param  attributes            The set of attributes that should be returned
2059   *                               in matching entries.  It may be {@code null}
2060   *                               or empty if the default attribute set (all
2061   *                               user attributes) is to be requested.
2062   *
2063   * @return  A search result object that provides information about the
2064   *          processing of the search, potentially including the set of
2065   *          matching entries and search references returned by the server.
2066   *
2067   * @throws  LDAPSearchException  If the search does not complete successfully,
2068   *                               or if a problem is encountered while sending
2069   *                               the request or reading the response.  If one
2070   *                               or more entries or references were returned
2071   *                               before the failure was encountered, then the
2072   *                               {@code LDAPSearchException} object may be
2073   *                               examined to obtain information about those
2074   *                               entries and/or references.
2075   */
2076  @Override()
2077  @NotNull()
2078  public final SearchResult search(
2079       @Nullable final SearchResultListener searchResultListener,
2080       @NotNull final String baseDN, @NotNull final SearchScope scope,
2081       @NotNull final DereferencePolicy derefPolicy, final int sizeLimit,
2082       final int timeLimit, final boolean typesOnly,
2083       @NotNull final Filter filter, @Nullable final String... attributes)
2084         throws LDAPSearchException
2085  {
2086    return search(new SearchRequest(searchResultListener, baseDN, scope,
2087         derefPolicy, sizeLimit, timeLimit, typesOnly, filter, attributes));
2088  }
2089
2090
2091
2092  /**
2093   * Processes the provided search request using a connection from this
2094   * connection pool.
2095   * <BR><BR>
2096   * Note that if the search does not complete successfully, an
2097   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2098   * search result entries or references may have been returned before the
2099   * failure response is received.  In this case, the
2100   * {@code LDAPSearchException} methods like {@code getEntryCount},
2101   * {@code getSearchEntries}, {@code getReferenceCount}, and
2102   * {@code getSearchReferences} may be used to obtain information about those
2103   * entries and references (although if a search result listener was provided,
2104   * then it will have been used to make any entries and references available,
2105   * and they will not be available through the {@code getSearchEntries} and
2106   * {@code getSearchReferences} methods).
2107   *
2108   * @param  searchRequest  The search request to be processed.  It must not be
2109   *                        {@code null}.
2110   *
2111   * @return  A search result object that provides information about the
2112   *          processing of the search, potentially including the set of
2113   *          matching entries and search references returned by the server.
2114   *
2115   * @throws  LDAPSearchException  If the search does not complete successfully,
2116   *                               or if a problem is encountered while sending
2117   *                               the request or reading the response.  If one
2118   *                               or more entries or references were returned
2119   *                               before the failure was encountered, then the
2120   *                               {@code LDAPSearchException} object may be
2121   *                               examined to obtain information about those
2122   *                               entries and/or references.
2123   */
2124  @Override()
2125  @NotNull()
2126  public final SearchResult search(@NotNull final SearchRequest searchRequest)
2127         throws LDAPSearchException
2128  {
2129    final LDAPConnection conn;
2130    try
2131    {
2132      conn = getConnection();
2133    }
2134    catch (final LDAPException le)
2135    {
2136      Debug.debugException(le);
2137      throw new LDAPSearchException(le);
2138    }
2139
2140    try
2141    {
2142      final SearchResult result = conn.search(searchRequest);
2143      releaseConnection(conn);
2144      return result;
2145    }
2146    catch (final Throwable t)
2147    {
2148      throwLDAPSearchExceptionIfShouldNotRetry(t, conn);
2149
2150      // If we have gotten here, then we should retry the operation with a
2151      // newly-created connection.
2152      final LDAPConnection newConn;
2153      try
2154      {
2155        newConn = replaceDefunctConnection(t, conn);
2156      }
2157      catch (final LDAPException le)
2158      {
2159        Debug.debugException(le);
2160        throw new LDAPSearchException(le);
2161      }
2162
2163      try
2164      {
2165        final SearchResult result = newConn.search(searchRequest);
2166        releaseConnection(newConn);
2167        return result;
2168      }
2169      catch (final Throwable t2)
2170      {
2171        throwLDAPSearchException(t2, newConn);
2172      }
2173
2174      // This return statement should never be reached.
2175      return null;
2176    }
2177  }
2178
2179
2180
2181  /**
2182   * Processes the provided search request using a connection from this
2183   * connection pool.
2184   * <BR><BR>
2185   * Note that if the search does not complete successfully, an
2186   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2187   * search result entries or references may have been returned before the
2188   * failure response is received.  In this case, the
2189   * {@code LDAPSearchException} methods like {@code getEntryCount},
2190   * {@code getSearchEntries}, {@code getReferenceCount}, and
2191   * {@code getSearchReferences} may be used to obtain information about those
2192   * entries and references (although if a search result listener was provided,
2193   * then it will have been used to make any entries and references available,
2194   * and they will not be available through the {@code getSearchEntries} and
2195   * {@code getSearchReferences} methods).
2196   *
2197   * @param  searchRequest  The search request to be processed.  It must not be
2198   *                        {@code null}.
2199   *
2200   * @return  A search result object that provides information about the
2201   *          processing of the search, potentially including the set of
2202   *          matching entries and search references returned by the server.
2203   *
2204   * @throws  LDAPSearchException  If the search does not complete successfully,
2205   *                               or if a problem is encountered while sending
2206   *                               the request or reading the response.  If one
2207   *                               or more entries or references were returned
2208   *                               before the failure was encountered, then the
2209   *                               {@code LDAPSearchException} object may be
2210   *                               examined to obtain information about those
2211   *                               entries and/or references.
2212   */
2213  @Override()
2214  @NotNull()
2215  public final SearchResult search(
2216                    @NotNull final ReadOnlySearchRequest searchRequest)
2217         throws LDAPSearchException
2218  {
2219    return search((SearchRequest) searchRequest);
2220  }
2221
2222
2223
2224  /**
2225   * Processes a search operation with the provided information using a
2226   * connection from this connection pool.  It is expected that at most one
2227   * entry will be returned from the search, and that no additional content from
2228   * the successful search result (e.g., diagnostic message or response
2229   * controls) are needed.
2230   * <BR><BR>
2231   * Note that if the search does not complete successfully, an
2232   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2233   * search result entries or references may have been returned before the
2234   * failure response is received.  In this case, the
2235   * {@code LDAPSearchException} methods like {@code getEntryCount},
2236   * {@code getSearchEntries}, {@code getReferenceCount}, and
2237   * {@code getSearchReferences} may be used to obtain information about those
2238   * entries and references.
2239   *
2240   * @param  baseDN      The base DN for the search request.  It must not be
2241   *                     {@code null}.
2242   * @param  scope       The scope that specifies the range of entries that
2243   *                     should be examined for the search.
2244   * @param  filter      The string representation of the filter to use to
2245   *                     identify matching entries.  It must not be
2246   *                     {@code null}.
2247   * @param  attributes  The set of attributes that should be returned in
2248   *                     matching entries.  It may be {@code null} or empty if
2249   *                     the default attribute set (all user attributes) is to
2250   *                     be requested.
2251   *
2252   * @return  The entry that was returned from the search, or {@code null} if no
2253   *          entry was returned or the base entry does not exist.
2254   *
2255   * @throws  LDAPSearchException  If the search does not complete successfully,
2256   *                               if more than a single entry is returned, or
2257   *                               if a problem is encountered while parsing the
2258   *                               provided filter string, sending the request,
2259   *                               or reading the response.  If one or more
2260   *                               entries or references were returned before
2261   *                               the failure was encountered, then the
2262   *                               {@code LDAPSearchException} object may be
2263   *                               examined to obtain information about those
2264   *                               entries and/or references.
2265   */
2266  @Override()
2267  @Nullable()
2268  public final SearchResultEntry searchForEntry(@NotNull final String baseDN,
2269                                      @NotNull final SearchScope scope,
2270                                      @NotNull final String filter,
2271                                      @Nullable final String... attributes)
2272         throws LDAPSearchException
2273  {
2274    return searchForEntry(new SearchRequest(baseDN, scope,
2275         DereferencePolicy.NEVER, 1, 0, false, parseFilter(filter),
2276         attributes));
2277  }
2278
2279
2280
2281  /**
2282   * Processes a search operation with the provided information using a
2283   * connection from this connection pool.  It is expected that at most one
2284   * entry will be returned from the search, and that no additional content from
2285   * the successful search result (e.g., diagnostic message or response
2286   * controls) are needed.
2287   * <BR><BR>
2288   * Note that if the search does not complete successfully, an
2289   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2290   * search result entries or references may have been returned before the
2291   * failure response is received.  In this case, the
2292   * {@code LDAPSearchException} methods like {@code getEntryCount},
2293   * {@code getSearchEntries}, {@code getReferenceCount}, and
2294   * {@code getSearchReferences} may be used to obtain information about those
2295   * entries and references.
2296   *
2297   * @param  baseDN      The base DN for the search request.  It must not be
2298   *                     {@code null}.
2299   * @param  scope       The scope that specifies the range of entries that
2300   *                     should be examined for the search.
2301   * @param  filter      The string representation of the filter to use to
2302   *                     identify matching entries.  It must not be
2303   *                     {@code null}.
2304   * @param  attributes  The set of attributes that should be returned in
2305   *                     matching entries.  It may be {@code null} or empty if
2306   *                     the default attribute set (all user attributes) is to
2307   *                     be requested.
2308   *
2309   * @return  The entry that was returned from the search, or {@code null} if no
2310   *          entry was returned or the base entry does not exist.
2311   *
2312   * @throws  LDAPSearchException  If the search does not complete successfully,
2313   *                               if more than a single entry is returned, or
2314   *                               if a problem is encountered while parsing the
2315   *                               provided filter string, sending the request,
2316   *                               or reading the response.  If one or more
2317   *                               entries or references were returned before
2318   *                               the failure was encountered, then the
2319   *                               {@code LDAPSearchException} object may be
2320   *                               examined to obtain information about those
2321   *                               entries and/or references.
2322   */
2323  @Override()
2324  @Nullable()
2325  public final SearchResultEntry searchForEntry(@NotNull final String baseDN,
2326                                      @NotNull final SearchScope scope,
2327                                      @NotNull final Filter filter,
2328                                      @Nullable final String... attributes)
2329         throws LDAPSearchException
2330  {
2331    return searchForEntry(new SearchRequest(baseDN, scope,
2332         DereferencePolicy.NEVER, 1, 0, false, filter, attributes));
2333  }
2334
2335
2336
2337  /**
2338   * Processes a search operation with the provided information using a
2339   * connection from this connection pool.  It is expected that at most one
2340   * entry will be returned from the search, and that no additional content from
2341   * the successful search result (e.g., diagnostic message or response
2342   * controls) are needed.
2343   * <BR><BR>
2344   * Note that if the search does not complete successfully, an
2345   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2346   * search result entries or references may have been returned before the
2347   * failure response is received.  In this case, the
2348   * {@code LDAPSearchException} methods like {@code getEntryCount},
2349   * {@code getSearchEntries}, {@code getReferenceCount}, and
2350   * {@code getSearchReferences} may be used to obtain information about those
2351   * entries and references.
2352   *
2353   * @param  baseDN       The base DN for the search request.  It must not be
2354   *                      {@code null}.
2355   * @param  scope        The scope that specifies the range of entries that
2356   *                      should be examined for the search.
2357   * @param  derefPolicy  The dereference policy the server should use for any
2358   *                      aliases encountered while processing the search.
2359   * @param  timeLimit    The maximum length of time in seconds that the server
2360   *                      should spend processing this search request.  A value
2361   *                      of zero indicates that there should be no limit.
2362   * @param  typesOnly    Indicates whether to return only attribute names in
2363   *                      matching entries, or both attribute names and values.
2364   * @param  filter       The string representation of the filter to use to
2365   *                      identify matching entries.  It must not be
2366   *                      {@code null}.
2367   * @param  attributes   The set of attributes that should be returned in
2368   *                      matching entries.  It may be {@code null} or empty if
2369   *                      the default attribute set (all user attributes) is to
2370   *                      be requested.
2371   *
2372   * @return  The entry that was returned from the search, or {@code null} if no
2373   *          entry was returned or the base entry does not exist.
2374   *
2375   * @throws  LDAPSearchException  If the search does not complete successfully,
2376   *                               if more than a single entry is returned, or
2377   *                               if a problem is encountered while parsing the
2378   *                               provided filter string, sending the request,
2379   *                               or reading the response.  If one or more
2380   *                               entries or references were returned before
2381   *                               the failure was encountered, then the
2382   *                               {@code LDAPSearchException} object may be
2383   *                               examined to obtain information about those
2384   *                               entries and/or references.
2385   */
2386  @Override()
2387  @Nullable()
2388  public final SearchResultEntry searchForEntry(@NotNull final String baseDN,
2389                    @NotNull final SearchScope scope,
2390                    @NotNull final DereferencePolicy derefPolicy,
2391                    final int timeLimit, final boolean typesOnly,
2392                    @NotNull final String filter,
2393                    @Nullable final String... attributes)
2394         throws LDAPSearchException
2395  {
2396    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2397         timeLimit, typesOnly, parseFilter(filter), attributes));
2398  }
2399
2400
2401
2402  /**
2403   * Processes a search operation with the provided information using a
2404   * connection from this connection pool.  It is expected that at most one
2405   * entry will be returned from the search, and that no additional content from
2406   * the successful search result (e.g., diagnostic message or response
2407   * controls) are needed.
2408   * <BR><BR>
2409   * Note that if the search does not complete successfully, an
2410   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2411   * search result entries or references may have been returned before the
2412   * failure response is received.  In this case, the
2413   * {@code LDAPSearchException} methods like {@code getEntryCount},
2414   * {@code getSearchEntries}, {@code getReferenceCount}, and
2415   * {@code getSearchReferences} may be used to obtain information about those
2416   * entries and references.
2417   *
2418   * @param  baseDN       The base DN for the search request.  It must not be
2419   *                      {@code null}.
2420   * @param  scope        The scope that specifies the range of entries that
2421   *                      should be examined for the search.
2422   * @param  derefPolicy  The dereference policy the server should use for any
2423   *                      aliases encountered while processing the search.
2424   * @param  timeLimit    The maximum length of time in seconds that the server
2425   *                      should spend processing this search request.  A value
2426   *                      of zero indicates that there should be no limit.
2427   * @param  typesOnly    Indicates whether to return only attribute names in
2428   *                      matching entries, or both attribute names and values.
2429   * @param  filter       The filter to use to identify matching entries.  It
2430   *                      must not be {@code null}.
2431   * @param  attributes   The set of attributes that should be returned in
2432   *                      matching entries.  It may be {@code null} or empty if
2433   *                      the default attribute set (all user attributes) is to
2434   *                      be requested.
2435   *
2436   * @return  The entry that was returned from the search, or {@code null} if no
2437   *          entry was returned or the base entry does not exist.
2438   *
2439   * @throws  LDAPSearchException  If the search does not complete successfully,
2440   *                               if more than a single entry is returned, or
2441   *                               if a problem is encountered while parsing the
2442   *                               provided filter string, sending the request,
2443   *                               or reading the response.  If one or more
2444   *                               entries or references were returned before
2445   *                               the failure was encountered, then the
2446   *                               {@code LDAPSearchException} object may be
2447   *                               examined to obtain information about those
2448   *                               entries and/or references.
2449   */
2450  @Override()
2451  @Nullable()
2452  public final SearchResultEntry searchForEntry(
2453                    @NotNull final String baseDN,
2454                    @NotNull final SearchScope scope,
2455                    @NotNull final DereferencePolicy derefPolicy,
2456                    final int timeLimit, final boolean typesOnly,
2457                    @NotNull final Filter filter,
2458                    @Nullable final String... attributes)
2459         throws LDAPSearchException
2460  {
2461    return searchForEntry(new SearchRequest(baseDN, scope, derefPolicy, 1,
2462         timeLimit, typesOnly, filter, attributes));
2463  }
2464
2465
2466
2467  /**
2468   * Processes a search operation with the provided information using a
2469   * connection from this connection pool.  It is expected that at most one
2470   * entry will be returned from the search, and that no additional content from
2471   * the successful search result (e.g., diagnostic message or response
2472   * controls) are needed.
2473   * <BR><BR>
2474   * Note that if the search does not complete successfully, an
2475   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2476   * search result entries or references may have been returned before the
2477   * failure response is received.  In this case, the
2478   * {@code LDAPSearchException} methods like {@code getEntryCount},
2479   * {@code getSearchEntries}, {@code getReferenceCount}, and
2480   * {@code getSearchReferences} may be used to obtain information about those
2481   * entries and references.
2482   *
2483   * @param  searchRequest  The search request to be processed.  If it is
2484   *                        configured with a search result listener or a size
2485   *                        limit other than one, then the provided request will
2486   *                        be duplicated with the appropriate settings.
2487   *
2488   * @return  The entry that was returned from the search, or {@code null} if no
2489   *          entry was returned or the base entry does not exist.
2490   *
2491   * @throws  LDAPSearchException  If the search does not complete successfully,
2492   *                               if more than a single entry is returned, or
2493   *                               if a problem is encountered while parsing the
2494   *                               provided filter string, sending the request,
2495   *                               or reading the response.  If one or more
2496   *                               entries or references were returned before
2497   *                               the failure was encountered, then the
2498   *                               {@code LDAPSearchException} object may be
2499   *                               examined to obtain information about those
2500   *                               entries and/or references.
2501   */
2502  @Override()
2503  @Nullable()
2504  public final SearchResultEntry searchForEntry(
2505                    @NotNull final SearchRequest searchRequest)
2506         throws LDAPSearchException
2507  {
2508    final LDAPConnection conn;
2509    try
2510    {
2511      conn = getConnection();
2512    }
2513    catch (final LDAPException le)
2514    {
2515      Debug.debugException(le);
2516      throw new LDAPSearchException(le);
2517    }
2518
2519    try
2520    {
2521      final SearchResultEntry entry = conn.searchForEntry(searchRequest);
2522      releaseConnection(conn);
2523      return entry;
2524    }
2525    catch (final Throwable t)
2526    {
2527      throwLDAPSearchExceptionIfShouldNotRetry(t, conn);
2528
2529      // If we have gotten here, then we should retry the operation with a
2530      // newly-created connection.
2531      final LDAPConnection newConn;
2532      try
2533      {
2534        newConn = replaceDefunctConnection(t, conn);
2535      }
2536      catch (final LDAPException le)
2537      {
2538        Debug.debugException(le);
2539        throw new LDAPSearchException(le);
2540      }
2541
2542      try
2543      {
2544        final SearchResultEntry entry = newConn.searchForEntry(searchRequest);
2545        releaseConnection(newConn);
2546        return entry;
2547      }
2548      catch (final Throwable t2)
2549      {
2550        throwLDAPSearchException(t2, newConn);
2551      }
2552
2553      // This return statement should never be reached.
2554      return null;
2555    }
2556  }
2557
2558
2559
2560  /**
2561   * Processes a search operation with the provided information using a
2562   * connection from this connection pool.  It is expected that at most one
2563   * entry will be returned from the search, and that no additional content from
2564   * the successful search result (e.g., diagnostic message or response
2565   * controls) are needed.
2566   * <BR><BR>
2567   * Note that if the search does not complete successfully, an
2568   * {@code LDAPSearchException} will be thrown  In some cases, one or more
2569   * search result entries or references may have been returned before the
2570   * failure response is received.  In this case, the
2571   * {@code LDAPSearchException} methods like {@code getEntryCount},
2572   * {@code getSearchEntries}, {@code getReferenceCount}, and
2573   * {@code getSearchReferences} may be used to obtain information about those
2574   * entries and references.
2575   *
2576   * @param  searchRequest  The search request to be processed.  If it is
2577   *                        configured with a search result listener or a size
2578   *                        limit other than one, then the provided request will
2579   *                        be duplicated with the appropriate settings.
2580   *
2581   * @return  The entry that was returned from the search, or {@code null} if no
2582   *          entry was returned or the base entry does not exist.
2583   *
2584   * @throws  LDAPSearchException  If the search does not complete successfully,
2585   *                               if more than a single entry is returned, or
2586   *                               if a problem is encountered while parsing the
2587   *                               provided filter string, sending the request,
2588   *                               or reading the response.  If one or more
2589   *                               entries or references were returned before
2590   *                               the failure was encountered, then the
2591   *                               {@code LDAPSearchException} object may be
2592   *                               examined to obtain information about those
2593   *                               entries and/or references.
2594   */
2595  @Override()
2596  @Nullable()
2597  public final SearchResultEntry searchForEntry(
2598                    @NotNull final ReadOnlySearchRequest searchRequest)
2599         throws LDAPSearchException
2600  {
2601    return searchForEntry((SearchRequest) searchRequest);
2602  }
2603
2604
2605
2606  /**
2607   * Parses the provided string as a {@code Filter} object.
2608   *
2609   * @param  filterString  The string to parse as a {@code Filter}.
2610   *
2611   * @return  The parsed {@code Filter}.
2612   *
2613   * @throws  LDAPSearchException  If the provided string does not represent a
2614   *                               valid search filter.
2615   */
2616  @NotNull()
2617  private static Filter parseFilter(@NotNull final String filterString)
2618          throws LDAPSearchException
2619  {
2620    try
2621    {
2622      return Filter.create(filterString);
2623    }
2624    catch (final LDAPException le)
2625    {
2626      Debug.debugException(le);
2627      throw new LDAPSearchException(le);
2628    }
2629  }
2630
2631
2632
2633  /**
2634   * Processes multiple requests in the order they are provided over a single
2635   * connection from this pool.  Note that the
2636   * {@link #retryFailedOperationsDueToInvalidConnections()} setting will be
2637   * ignored when processing the provided operations, so that any failed
2638   * operations will not be retried.
2639   *
2640   * @param  requests         The list of requests to be processed.  It must not
2641   *                          be {@code null} or empty.
2642   * @param  continueOnError  Indicates whether to attempt to process subsequent
2643   *                          requests if any of the operations does not
2644   *                          complete successfully.
2645   *
2646   * @return  The set of results from the requests that were processed.  The
2647   *          order of result objects will correspond to the order of the
2648   *          request objects, although the list of results may contain fewer
2649   *          elements than the list of requests if an error occurred during
2650   *          processing and {@code continueOnError} is {@code false}.
2651   *
2652   * @throws  LDAPException  If a problem occurs while trying to obtain a
2653   *                         connection to use for the requests.
2654   */
2655  @NotNull()
2656  public final List<LDAPResult> processRequests(
2657                                     @NotNull final List<LDAPRequest> requests,
2658                                     final boolean continueOnError)
2659         throws LDAPException
2660  {
2661    Validator.ensureNotNull(requests);
2662    Validator.ensureFalse(requests.isEmpty(),
2663         "LDAPConnectionPool.processRequests.requests must not be empty.");
2664
2665    final LDAPConnection conn;
2666    try
2667    {
2668      conn = getConnection();
2669    }
2670    catch (final LDAPException le)
2671    {
2672      Debug.debugException(le);
2673      throw new LDAPSearchException(le);
2674    }
2675
2676    final ArrayList<LDAPResult> results = new ArrayList<>(requests.size());
2677    boolean isDefunct = false;
2678
2679    try
2680    {
2681requestLoop:
2682      for (final LDAPRequest request : requests)
2683      {
2684        try
2685        {
2686          final LDAPResult result = conn.processOperation(request);
2687          results.add(result);
2688          switch (result.getResultCode().intValue())
2689          {
2690            case ResultCode.SUCCESS_INT_VALUE:
2691            case ResultCode.COMPARE_FALSE_INT_VALUE:
2692            case ResultCode.COMPARE_TRUE_INT_VALUE:
2693            case ResultCode.NO_OPERATION_INT_VALUE:
2694              // These will be considered successful operations.
2695              break;
2696
2697            default:
2698              // Anything else will be considered a failure.
2699              if (! ResultCode.isConnectionUsable(result.getResultCode()))
2700              {
2701                isDefunct = true;
2702              }
2703
2704              if (! continueOnError)
2705              {
2706                break requestLoop;
2707              }
2708              break;
2709          }
2710        }
2711        catch (final LDAPException le)
2712        {
2713          Debug.debugException(le);
2714          results.add(new LDAPResult(request.getLastMessageID(),
2715                                     le.getResultCode(), le.getMessage(),
2716                                     le.getMatchedDN(), le.getReferralURLs(),
2717                                     le.getResponseControls()));
2718
2719          if (! ResultCode.isConnectionUsable(le.getResultCode()))
2720          {
2721            isDefunct = true;
2722          }
2723
2724          if (! continueOnError)
2725          {
2726            break;
2727          }
2728        }
2729      }
2730    }
2731    finally
2732    {
2733      if (isDefunct)
2734      {
2735        releaseDefunctConnection(conn);
2736      }
2737      else
2738      {
2739        releaseConnection(conn);
2740      }
2741    }
2742
2743    return results;
2744  }
2745
2746
2747
2748  /**
2749   * Processes multiple requests over a single connection from this pool using
2750   * asynchronous processing to cause the operations to be processed
2751   * concurrently.  The list of requests may contain only add, compare, delete,
2752   * modify, modify DN, and search operations (and any search operations to be
2753   * processed must be configured with an {@link AsyncSearchResultListener}.
2754   * This method will not return until all operations have completed, or until
2755   * the specified timeout period has elapsed.  The order of elements in the
2756   * list of the {@link AsyncRequestID} objects returned will correspond to the
2757   * order of elements in the list of requests.  The operation results may be
2758   * obtained from the returned {@code AsyncRequestID} objects using the
2759   * {@code java.util.concurrent.Future} API.
2760   *
2761   * @param  requests           The list of requests to be processed.  It must
2762   *                            not be {@code null} or empty, and it must
2763   *                            contain only add, compare, modify, modify DN,
2764   *                            and search requests.  Any search requests must
2765   *                            be configured with an
2766   *                            {@code AsyncSearchResultListener}.
2767   * @param  maxWaitTimeMillis  The maximum length of time in milliseconds to
2768   *                            wait for the operations to complete before
2769   *                            returning.  A value that is less than or equal
2770   *                            to zero indicates that the client should wait
2771   *                            indefinitely for the operations to complete.
2772   *
2773   * @return  The list of {@code AsyncRequestID} objects that may be used to
2774   *          retrieve the results for the operations.  The order of elements in
2775   *          this list will correspond to the order of the provided requests.
2776   *
2777   * @throws  LDAPException  If there is a problem with any of the requests, or
2778   *                         if connections in the pool are configured to use
2779   *                         synchronous mode and therefore cannot be used to
2780   *                         process asynchronous operations.
2781   */
2782  @NotNull()
2783  public final List<AsyncRequestID> processRequestsAsync(
2784                    @NotNull final List<LDAPRequest> requests,
2785                    final long maxWaitTimeMillis)
2786         throws LDAPException
2787  {
2788    // Make sure the set of requests is not null or empty.
2789    Validator.ensureNotNull(requests);
2790    Validator.ensureFalse(requests.isEmpty(),
2791         "LDAPConnectionPool.processRequests.requests must not be empty.");
2792
2793    // Make sure that all the requests are acceptable.
2794    for (final LDAPRequest r : requests)
2795    {
2796      switch (r.getOperationType())
2797      {
2798        case ADD:
2799        case COMPARE:
2800        case DELETE:
2801        case MODIFY:
2802        case MODIFY_DN:
2803          // These operation types are always acceptable for asynchronous
2804          // processing.
2805          break;
2806
2807        case SEARCH:
2808          // Search operations will only be acceptable if they have been
2809          // configured with an async search result listener.
2810          final SearchRequest searchRequest = (SearchRequest) r;
2811          if ((searchRequest.getSearchResultListener() == null) ||
2812              (! (searchRequest.getSearchResultListener() instanceof
2813                   AsyncSearchResultListener)))
2814          {
2815            throw new LDAPException(ResultCode.PARAM_ERROR,
2816                 ERR_POOL_PROCESS_REQUESTS_ASYNC_SEARCH_NOT_ASYNC.get(
2817                      String.valueOf(r)));
2818          }
2819          break;
2820
2821        case ABANDON:
2822        case BIND:
2823        case EXTENDED:
2824        case UNBIND:
2825        default:
2826          // These operation types are never acceptable for asynchronous
2827          // processing.
2828          throw new LDAPException(ResultCode.PARAM_ERROR,
2829               ERR_POOL_PROCESS_REQUESTS_ASYNC_OP_NOT_ASYNC.get(
2830                    String.valueOf(r)));
2831      }
2832    }
2833
2834
2835    final LDAPConnection conn;
2836    try
2837    {
2838      conn = getConnection();
2839    }
2840    catch (final LDAPException le)
2841    {
2842      Debug.debugException(le);
2843      throw new LDAPSearchException(le);
2844    }
2845
2846
2847    final ArrayList<AsyncRequestID> requestIDs =
2848         new ArrayList<>(requests.size());
2849    boolean isDefunct = false;
2850
2851    try
2852    {
2853      // Make sure that the connection is not configured to use synchronous
2854      // mode, because asynchronous operations are not allowed in that mode.
2855      if (conn.synchronousMode())
2856      {
2857        throw new LDAPException(ResultCode.PARAM_ERROR,
2858             ERR_POOL_PROCESS_REQUESTS_ASYNC_SYNCHRONOUS_MODE.get());
2859      }
2860
2861
2862      // Issue all of the requests.  If an exception is encountered while
2863      // issuing a request, then convert it into an AsyncRequestID with the
2864      // exception as the result.
2865      for (final LDAPRequest r : requests)
2866      {
2867        AsyncRequestID requestID = null;
2868        try
2869        {
2870          switch (r.getOperationType())
2871          {
2872            case ADD:
2873              requestID = conn.asyncAdd((AddRequest) r, null);
2874              break;
2875            case COMPARE:
2876              requestID = conn.asyncCompare((CompareRequest) r, null);
2877              break;
2878            case DELETE:
2879              requestID = conn.asyncDelete((DeleteRequest) r, null);
2880              break;
2881            case MODIFY:
2882              requestID = conn.asyncModify((ModifyRequest) r, null);
2883              break;
2884            case MODIFY_DN:
2885              requestID = conn.asyncModifyDN((ModifyDNRequest) r, null);
2886              break;
2887            case SEARCH:
2888              requestID = conn.asyncSearch((SearchRequest) r);
2889              break;
2890          }
2891        }
2892        catch (final LDAPException le)
2893        {
2894          Debug.debugException(le);
2895          requestID = new AsyncRequestID(r.getLastMessageID(), conn);
2896          requestID.setResult(le.toLDAPResult());
2897        }
2898
2899        requestIDs.add(requestID);
2900      }
2901
2902
2903      // Wait for the operations to complete.  If any operation does not
2904      // complete before the specified timeout, then create a failure result for
2905      // it.  If any operation does not complete successfully, then attempt to
2906      // determine whether the failure may indicate that the connection is no
2907      // longer valid.
2908      final long startWaitingTime = System.currentTimeMillis();
2909      final long stopWaitingTime;
2910      if (maxWaitTimeMillis > 0)
2911      {
2912        stopWaitingTime = startWaitingTime + maxWaitTimeMillis;
2913      }
2914      else
2915      {
2916        stopWaitingTime = Long.MAX_VALUE;
2917      }
2918
2919      for (final AsyncRequestID requestID : requestIDs)
2920      {
2921        LDAPResult result;
2922        final long waitTime = stopWaitingTime - System.currentTimeMillis();
2923        if (waitTime > 0)
2924        {
2925          try
2926          {
2927            result = requestID.get(waitTime, TimeUnit.MILLISECONDS);
2928          }
2929          catch (final Exception e)
2930          {
2931            Debug.debugException(e);
2932            requestID.cancel(true);
2933
2934            if (e instanceof TimeoutException)
2935            {
2936              result = new LDAPResult(requestID.getMessageID(),
2937                   ResultCode.TIMEOUT,
2938                   ERR_POOL_PROCESS_REQUESTS_ASYNC_RESULT_TIMEOUT.get(
2939                        (System.currentTimeMillis() - startWaitingTime)),
2940                   null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
2941            }
2942            else
2943            {
2944              result = new LDAPResult(requestID.getMessageID(),
2945                   ResultCode.LOCAL_ERROR,
2946                   ERR_POOL_PROCESS_REQUESTS_ASYNC_RESULT_EXCEPTION.get(
2947                        StaticUtils.getExceptionMessage(e)),
2948                   null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
2949            }
2950            requestID.setResult(result);
2951          }
2952        }
2953        else
2954        {
2955          requestID.cancel(true);
2956          result = new LDAPResult(requestID.getMessageID(),
2957               ResultCode.TIMEOUT,
2958               ERR_POOL_PROCESS_REQUESTS_ASYNC_RESULT_TIMEOUT.get(
2959                    (System.currentTimeMillis() - startWaitingTime)),
2960               null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS);
2961          requestID.setResult(result);
2962        }
2963
2964
2965        // See if we think that the connection may be defunct.
2966        if (! ResultCode.isConnectionUsable(result.getResultCode()))
2967        {
2968          isDefunct = true;
2969        }
2970      }
2971
2972      return requestIDs;
2973    }
2974    finally
2975    {
2976      if (isDefunct)
2977      {
2978        releaseDefunctConnection(conn);
2979      }
2980      else
2981      {
2982        releaseConnection(conn);
2983      }
2984    }
2985  }
2986
2987
2988
2989  /**
2990   * Examines the provided {@code Throwable} object to determine whether it
2991   * represents an {@code LDAPException} that indicates the associated
2992   * connection may no longer be valid.  If that is the case, and if such
2993   * operations should be retried, then no exception will be thrown.  Otherwise,
2994   * an appropriate {@code LDAPException} will be thrown.
2995   *
2996   * @param  t     The {@code Throwable} object that was caught.
2997   * @param  o     The type of operation for which to make the determination.
2998   * @param  conn  The connection to be released to the pool.
2999   *
3000   * @throws  LDAPException  To indicate that a problem occurred during LDAP
3001   *                         processing and the operation should not be retried.
3002   */
3003  private void throwLDAPExceptionIfShouldNotRetry(@NotNull final Throwable t,
3004                    @NotNull final OperationType o,
3005                    @NotNull final LDAPConnection conn)
3006          throws LDAPException
3007  {
3008    if ((t instanceof LDAPException) &&
3009        getOperationTypesToRetryDueToInvalidConnections().contains(o))
3010    {
3011      final LDAPException le = (LDAPException) t;
3012      final LDAPConnectionPoolHealthCheck healthCheck = getHealthCheck();
3013
3014      try
3015      {
3016        healthCheck.ensureConnectionValidAfterException(conn, le);
3017      }
3018      catch (final Exception e)
3019      {
3020        // If we have gotten this exception, then it indicates that the
3021        // connection is no longer valid and the operation should be retried.
3022        Debug.debugException(e);
3023        return;
3024      }
3025    }
3026
3027    throwLDAPException(t, conn);
3028  }
3029
3030
3031
3032  /**
3033   * Examines the provided {@code Throwable} object to determine whether it
3034   * represents an {@code LDAPException} that indicates the associated
3035   * connection may no longer be valid.  If that is the case, and if such
3036   * operations should be retried, then no exception will be thrown.  Otherwise,
3037   * an appropriate {@code LDAPSearchException} will be thrown.
3038   *
3039   * @param  t     The {@code Throwable} object that was caught.
3040   * @param  conn  The connection to be released to the pool.
3041   *
3042   * @throws  LDAPSearchException  To indicate that a problem occurred during
3043   *                               LDAP processing and the operation should not
3044   *                               be retried.
3045   */
3046  private void throwLDAPSearchExceptionIfShouldNotRetry(
3047                    @NotNull final Throwable t,
3048                    @NotNull final LDAPConnection conn)
3049          throws LDAPSearchException
3050  {
3051    if ((t instanceof LDAPException) &&
3052        getOperationTypesToRetryDueToInvalidConnections().contains(
3053             OperationType.SEARCH))
3054    {
3055      final LDAPException le = (LDAPException) t;
3056      final LDAPConnectionPoolHealthCheck healthCheck = getHealthCheck();
3057
3058      try
3059      {
3060        healthCheck.ensureConnectionValidAfterException(conn, le);
3061      }
3062      catch (final Exception e)
3063      {
3064        // If we have gotten this exception, then it indicates that the
3065        // connection is no longer valid and the operation should be retried.
3066        Debug.debugException(e);
3067        return;
3068      }
3069    }
3070
3071    throwLDAPSearchException(t, conn);
3072  }
3073
3074
3075
3076  /**
3077   * Handles the provided {@code Throwable} object by ensuring that the provided
3078   * connection is released to the pool and throwing an appropriate
3079   * {@code LDAPException} object.
3080   *
3081   * @param  t     The {@code Throwable} object that was caught.
3082   * @param  conn  The connection to be released to the pool.
3083   *
3084   * @throws  LDAPException  To indicate that a problem occurred during LDAP
3085   *                         processing.
3086   */
3087  void throwLDAPException(@NotNull final Throwable t,
3088                          @NotNull final LDAPConnection conn)
3089       throws LDAPException
3090  {
3091    Debug.debugException(t);
3092    if (t instanceof LDAPException)
3093    {
3094      final LDAPException le = (LDAPException) t;
3095      releaseConnectionAfterException(conn, le);
3096      throw le;
3097    }
3098    else
3099    {
3100      releaseDefunctConnection(conn);
3101      StaticUtils.rethrowIfError(t);
3102      throw new LDAPException(ResultCode.LOCAL_ERROR,
3103           ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t);
3104    }
3105  }
3106
3107
3108
3109  /**
3110   * Handles the provided {@code Throwable} object by ensuring that the provided
3111   * connection is released to the pool and throwing an appropriate
3112   * {@code LDAPSearchException} object.
3113   *
3114   * @param  t     The {@code Throwable} object that was caught.
3115   * @param  conn  The connection to be released to the pool.
3116   *
3117   * @throws  LDAPSearchException  To indicate that a problem occurred during
3118   *                               LDAP search processing.
3119   */
3120  void throwLDAPSearchException(@NotNull final Throwable t,
3121                                @NotNull final LDAPConnection conn)
3122       throws LDAPSearchException
3123  {
3124    Debug.debugException(t);
3125    if (t instanceof LDAPException)
3126    {
3127      final LDAPSearchException lse;
3128      if (t instanceof LDAPSearchException)
3129      {
3130        lse = (LDAPSearchException) t;
3131      }
3132      else
3133      {
3134        lse = new LDAPSearchException((LDAPException) t);
3135      }
3136
3137      releaseConnectionAfterException(conn, lse);
3138      throw lse;
3139    }
3140    else
3141    {
3142      releaseDefunctConnection(conn);
3143      StaticUtils.rethrowIfError(t);
3144      throw new LDAPSearchException(ResultCode.LOCAL_ERROR,
3145           ERR_POOL_OP_EXCEPTION.get(StaticUtils.getExceptionMessage(t)), t);
3146    }
3147  }
3148
3149
3150
3151  /**
3152   * Retrieves a string representation of this connection pool.
3153   *
3154   * @return  A string representation of this connection pool.
3155   */
3156  @Override()
3157  @NotNull()
3158  public final String toString()
3159  {
3160    final StringBuilder buffer = new StringBuilder();
3161    toString(buffer);
3162    return buffer.toString();
3163  }
3164
3165
3166
3167  /**
3168   * Appends a string representation of this connection pool to the provided
3169   * buffer.
3170   *
3171   * @param  buffer  The buffer to which the string representation should be
3172   *                 appended.
3173   */
3174  public abstract void toString(@NotNull StringBuilder buffer);
3175}