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 com.unboundid.util.Extensible;
041import com.unboundid.util.NotNull;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045
046import static com.unboundid.ldap.sdk.LDAPMessages.*;
047
048
049
050/**
051 * This class provides an API that may be used to determine whether connections
052 * associated with a connection pool are valid and suitable for use.  It
053 * provides the ability to check the validity of a connection at the following
054 * times:
055 * <UL>
056 *   <LI>Whenever a new connection is created for use in the pool, the
057 *       {@link #ensureNewConnectionValid(LDAPConnection)} method will be called
058 *       before making that connection available.  The default implementation
059 *       provided in this class does not perform any kind of processing, but
060 *       subclasses may override this behavior if desired.</LI>
061 *   <LI>Whenever a connection is checked out from the pool (including
062 *       connections checked out internally for operations performed in the
063 *       pool), the {@link #ensureConnectionValidForCheckout(LDAPConnection)}
064 *       method will be called.  The default implementation provided in this
065 *       class does not perform any kind of processing, but subclasses may
066 *       override this behavior if desired.</LI>
067 *   <LI>Whenever a connection is released back to the pool (including
068 *       connections checked out internally for operations performed in the
069 *       pool), the {@link #ensureConnectionValidForRelease(LDAPConnection)}
070 *       method will be called.  The default implementation provided in this
071 *       class does not perform any kind of processing, but subclasses may
072 *       override this behavior if desired.</LI>
073 *   <LI>The {@link #ensureConnectionValidForContinuedUse(LDAPConnection)}
074 *       method will be invoked periodically by a background thread created by
075 *       the connection pool to determine whether available connections within
076 *       the pool are still valid.  The default implementation provided in this
077 *       class does not perform any kind of processing, but subclasses may
078 *       override this behavior if desired.</LI>
079 *   <LI>The {@link #ensureConnectionValidAfterException} method may be invoked
080 *       if an exception is caught while processing an operation with a
081 *       connection that is part of a connection pool.  The default
082 *       implementation provided in this class only examines the result code of
083 *       the provided exception and uses the
084 *       {@link ResultCode#isConnectionUsable(ResultCode)} method to make the
085 *       determination, but subclasses may override this behavior if
086 *       desired.</LI>
087 * </UL>
088 * Note that health check implementations should be designed so that they are
089 * suitable for use with connections having any authentication state.  The
090 * {@link #ensureNewConnectionValid(LDAPConnection)} method will be invoked on
091 * unauthenticated connections, and the remaining health check methods will be
092 * invoked using whatever credentials are assigned to connections in the
093 * associated connection pool.
094 */
095@Extensible()
096@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE)
097public class LDAPConnectionPoolHealthCheck
098{
099  /**
100   * Creates a new instance of this LDAP connection pool health check.
101   */
102  public LDAPConnectionPoolHealthCheck()
103  {
104    // No implementation is required.
105  }
106
107
108
109  /**
110   * Performs any desired processing to determine whether the provided new
111   * connection is available to be checked out and used for processing
112   * operations.  This method will be invoked by either {@link ServerSet} used
113   * by the connection pool (if it supports enhanced health checking) or by the
114   * connection pool itself at the time that a new connection is created.  No
115   * authentication will have been performed on this connection at the time the
116   * health check is invoked.
117   *
118   * @param  connection  The connection to be examined.
119   *
120   * @throws  LDAPException  If a problem is detected that suggests that the
121   *                         provided connection is not suitable for use.
122   */
123  public void ensureNewConnectionValid(@NotNull final LDAPConnection connection)
124         throws LDAPException
125  {
126    // No processing is performed in this default implementation.
127  }
128
129
130
131  /**
132   * Performs any desired processing to determine whether the provided
133   * connection is valid after processing a bind operation with the provided
134   * result.
135   * <BR><BR>
136   * This method will be invoked under the following circumstances:
137   * <UL>
138   *   <LI>
139   *     If you create a connection pool with a {@link ServerSet} and a
140   *     non-{@code null} {@link BindRequest}, then this health check method
141   *     will be invoked for every new connection created by the pool after
142   *     processing that {@code BindRequest} on the connection.  If you create a
143   *     connection pool with a {@code ServerSet} but a {@code null}
144   *     {@code BindRequest}, then no authentication will be attempted (and
145   *     therefore this health check method will not be invoked for)
146   *     newly-created connections.
147   *   </LI>
148   *   <LI>
149   *     If you create a connection pool with an {@link LDAPConnection} after
150   *     having performed a bind operation on that connection, then every new
151   *     connection created by the pool will attempt to perform the same type of
152   *     bind operation and this health check method will be invoked after that
153   *     bind attempt has completed.  If you create a connection pool with an
154   *     {@code LDAPConnection} that has not been authenticated, then no
155   *     authentication will be attempted (and therefore this health check
156   *     method will not be invoked for) newly-created connections.
157   *   </LI>
158   *   <LI>
159   *     If you call a connection pool's {@code bindAndRevertAuthentication}
160   *     method, then this health check method will be called after the second
161   *     bind operation (the one used to revert authentication) has completed.
162   *     In this case, this health check method will be called even if the
163   *     connection pool was created with a {@code null} {@code BindRequest} or
164   *     with an unauthenticated {@code LDAPConnection}.  In that case, the
165   *     bind operation used to revert authentication will be a
166   *     {@link SimpleBindRequest} with an empty DN and password.
167   *   </LI>
168   *   <LI>
169   *     If you call a connection pool's
170   *     {@code releaseAndReAuthenticateConnection} method, then this health
171   *     check method will be called after the bind operation has completed.  As
172   *     with {@code bindAndRevertAuthentication}, this health check method will
173   *     be called even if the connection pool was created with a {@code null}
174   *     {@code BindRequest} or with an unauthenticated {@code LDAPConnection}.
175   *   </LI>
176   * </UL>
177   * <BR><BR>
178   * Note that this health check method may be invoked even if the bind
179   * attempt was not successful.  This is useful because it allows the health
180   * check to intercept a failed authentication attempt and differentiate it
181   * from other types of failures in the course of trying to create or check out
182   * a connection.  In the event that it is invoked with a {@code BindResult}
183   * that has a result code other than {@link ResultCode#SUCCESS}, if this
184   * method throws an exception then that exception will be propagated to the
185   * caller.  If this method does not throw an exception when provided with a
186   * non-{@code SUCCESS} result, then the connection pool itself will throw an
187   * exception using the information in the bind result.
188   *
189   * @param  connection  The connection to be examined.
190   * @param  bindResult  The bind result obtained from the authentication
191   *                     process.
192   *
193   * @throws  LDAPException  If a problem is detected that suggests that the
194   *                         provided connection is not suitable for use.
195   */
196  public void ensureConnectionValidAfterAuthentication(
197                   @NotNull final LDAPConnection connection,
198                   @NotNull final BindResult bindResult)
199         throws LDAPException
200  {
201    // No processing is performed in this default implementation.
202  }
203
204
205
206  /**
207   * Performs any desired processing to determine whether the provided
208   * connection is available to be checked out and used for processing
209   * operations.  This method will be invoked by the
210   * {@link LDAPConnectionPool#getConnection()} method before handing out a
211   * connection.  This method should return normally if the connection is
212   * believed to be valid, or should throw an {@code LDAPException} if a problem
213   * is detected.
214   *
215   * @param  connection  The connection to be examined.
216   *
217   * @throws  LDAPException  If a problem is detected that suggests that the
218   *                         provided connection is not suitable for use.
219   */
220  public void ensureConnectionValidForCheckout(
221                    @NotNull final LDAPConnection connection)
222         throws LDAPException
223  {
224    // No processing is performed in this default implementation.
225  }
226
227
228
229  /**
230   * Performs any desired processing to determine whether the provided
231   * connection is valid and should be released back to the pool to be used for
232   * processing other operations.  This method will be invoked by the
233   * {@link LDAPConnectionPool#releaseConnection(LDAPConnection)} method before
234   * making the connection available for use in processing other operations.
235   * This method should return normally if the connection is believed to be
236   * valid, or should throw an {@code LDAPException} if a problem is detected.
237   *
238   * @param  connection  The connection to be examined.
239   *
240   * @throws  LDAPException  If a problem is detected that suggests that the
241   *                         provided connection is not suitable for use.
242   */
243  public void ensureConnectionValidForRelease(
244                   @NotNull final LDAPConnection connection)
245         throws LDAPException
246  {
247    // No processing is performed in this default implementation.
248  }
249
250
251
252  /**
253   * Performs any desired processing to determine whether the provided
254   * connection is valid and should continue to be made available for
255   * processing operations.  This method will be periodically invoked by a
256   * background thread used to test availability of connections within the pool.
257   * This method should return normally if the connection is believed to be
258   * valid, or should throw an {@code LDAPException} if a problem is detected.
259   *
260   * @param  connection  The connection to be examined.
261   *
262   * @throws  LDAPException  If a problem is detected that suggests that the
263   *                         provided connection is not suitable for use.
264   */
265  public void ensureConnectionValidForContinuedUse(
266                   @NotNull final LDAPConnection connection)
267         throws LDAPException
268  {
269    // No processing is performed in this default implementation.
270  }
271
272
273
274  /**
275   * Performs any processing that may be appropriate on an ongoing basis for the
276   * connection pool that is related to the pool itself rather than any
277   * individual connection.  This method will be invoked by the pool's
278   * {@link LDAPConnectionPoolHealthCheckThread} at an interval specified by the
279   * pool's {@link AbstractConnectionPool#getHealthCheckIntervalMillis()}
280   * method.  This method will be invoked after all other periodic processing
281   * (for example, after calling {@link #ensureConnectionValidForContinuedUse}
282   * on each available connection, if appropriate for the pool implementation)
283   * has been performed during the interval.
284   *
285   * @param  pool  The connection pool on which to perform maintenance.
286   */
287  public void performPoolMaintenance(@NotNull final AbstractConnectionPool pool)
288  {
289    // No processing is performed in this default implementation.
290  }
291
292
293
294  /**
295   * Indicates whether the provided connection may still be considered valid
296   * after an attempt to process an operation yielded the given exception.  This
297   * method will be invoked by the
298   * {@link LDAPConnectionPool#releaseConnectionAfterException} method, and it
299   * may also be manually invoked by external callers if an exception is
300   * encountered while processing an operation on a connection checked out from
301   * the pool.  It may make a determination based solely on the provided
302   * exception, or it may also attempt to use the provided connection to further
303   * test its validity.  This method should return normally if the connection is
304   * believed to be valid, or should throw an {@code LDAPException} if a problem
305   * is detected.
306   *
307   * @param  connection  The connection to be examined.
308   * @param  exception   The exception that was caught while processing an
309   *                     operation on the connection.
310   *
311   * @throws  LDAPException  If a problem is detected that suggests that the
312   *                         provided connection is not suitable for use.
313   */
314  public void ensureConnectionValidAfterException(
315                   @NotNull final LDAPConnection connection,
316                   @NotNull final LDAPException exception)
317         throws LDAPException
318  {
319    if (! ResultCode.isConnectionUsable(exception.getResultCode()))
320    {
321      throw new LDAPException(ResultCode.SERVER_DOWN,
322           ERR_POOL_HEALTH_CHECK_CONN_INVALID_AFTER_EXCEPTION.get(
323                StaticUtils.getExceptionMessage(exception)),
324           exception);
325    }
326  }
327
328
329
330  /**
331   * Retrieves a string representation of this LDAP connection pool health
332   * check.
333   *
334   * @return  A string representation of this LDAP connection pool health check.
335   */
336  @Override()
337  @NotNull()
338  public final String toString()
339  {
340    final StringBuilder buffer = new StringBuilder();
341    toString(buffer);
342    return buffer.toString();
343  }
344
345
346
347  /**
348   * Appends a string representation of this LDAP connection pool health check
349   * to the provided buffer.
350   *
351   * @param  buffer  The buffer to which the information should be appended.
352   */
353  public void toString(@NotNull final StringBuilder buffer)
354  {
355    buffer.append("LDAPConnectionPoolHealthCheck()");
356  }
357}