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