001/*
002 * Copyright 2009-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2009-2017 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.ldap.sdk;
022
023
024
025import java.io.Serializable;
026
027import com.unboundid.util.NotMutable;
028import com.unboundid.util.ThreadSafety;
029import com.unboundid.util.ThreadSafetyLevel;
030
031import static com.unboundid.ldap.sdk.LDAPMessages.*;
032import static com.unboundid.util.Debug.*;
033import static com.unboundid.util.StaticUtils.*;
034
035
036
037/**
038 * This class provides an LDAP connection pool health check implementation that
039 * may be used to check the health of the associated server by verifying that a
040 * specified entry can be retrieved in an acceptable period of time.  If the
041 * entry cannot be retrieved (either because it does not exist, or because an
042 * error occurs while attempting to retrieve it), or if it takes too long to
043 * retrieve the entry, then the associated connection will be classified as
044 * unavailable.
045 * <BR><BR>
046 * It is possible to control under which conditions an attempt should be made to
047 * retrieve the target entry, and also to specify a maximum acceptable response
048 * time.  For best results, the target entry should be available to be retrieved
049 * by a client with any authentication state.
050 */
051@NotMutable()
052@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
053public final class GetEntryLDAPConnectionPoolHealthCheck
054       extends LDAPConnectionPoolHealthCheck
055       implements Serializable
056{
057  /**
058   * The default maximum response time value in milliseconds, which is set to
059   * 30,000 milliseconds or 30 seconds.
060   */
061  private static final long DEFAULT_MAX_RESPONSE_TIME = 30000L;
062
063
064
065  /**
066   * The serial version UID for this serializable class.
067   */
068  private static final long serialVersionUID = -3400259782503254645L;
069
070
071
072  // Indicates whether to invoke the test during background health checks.
073  private final boolean invokeForBackgroundChecks;
074
075  // Indicates whether to invoke the test when checking out a connection.
076  private final boolean invokeOnCheckout;
077
078  // Indicates whether to invoke the test when creating a new connection.
079  private final boolean invokeOnCreate;
080
081  // Indicates whether to invoke the test whenever an exception is encountered
082  // when using the connection.
083  private final boolean invokeOnException;
084
085  // Indicates whether to invoke the test when releasing a connection.
086  private final boolean invokeOnRelease;
087
088  // The maximum response time value in milliseconds.
089  private final long maxResponseTime;
090
091  // The search request to send to the server.
092  private final SearchRequest searchRequest;
093
094  // The DN of the entry to retrieve.
095  private final String entryDN;
096
097
098
099  /**
100   * Creates a new instance of this get entry LDAP connection pool health check.
101   *
102   * @param  entryDN                    The DN of the entry to retrieve from
103   *                                    the target server.  If this is
104   *                                    {@code null}, then the server's root DSE
105   *                                    will be used.
106   * @param  maxResponseTime            The maximum length of time in
107   *                                    milliseconds that should be allowed when
108   *                                    attempting to retrieve the entry.  If
109   *                                    the provided value is less than or equal
110   *                                    to zero, then the default value of 30000
111   *                                    milliseconds (30 seconds) will be used.
112   * @param  invokeOnCreate             Indicates whether to test for the
113   *                                    existence of the target entry whenever a
114   *                                    new connection is created for use in the
115   *                                    pool.
116   * @param  invokeOnCheckout           Indicates whether to test for the
117   *                                    existence of the target entry
118   *                                    immediately before a connection is
119   *                                    checked out of the pool.
120   * @param  invokeOnRelease            Indicates whether to test for the
121   *                                    existence of the target entry
122   *                                    immediately after a connection has been
123   *                                    released back to the pool.
124   * @param  invokeForBackgroundChecks  Indicates whether to test for the
125   *                                    existence of the target entry during
126   *                                    periodic background health checks.
127   * @param  invokeOnException          Indicates whether to test for the
128   *                                    existence of the target entry if an
129   *                                    exception is encountered when using the
130   *                                    connection.
131   */
132  public GetEntryLDAPConnectionPoolHealthCheck(final String entryDN,
133              final long maxResponseTime, final boolean invokeOnCreate,
134              final boolean invokeOnCheckout, final boolean invokeOnRelease,
135              final boolean invokeForBackgroundChecks,
136              final boolean invokeOnException)
137  {
138    this.invokeOnCreate            = invokeOnCreate;
139    this.invokeOnCheckout          = invokeOnCheckout;
140    this.invokeOnRelease           = invokeOnRelease;
141    this.invokeForBackgroundChecks = invokeForBackgroundChecks;
142    this.invokeOnException         = invokeOnException;
143
144    if (entryDN == null)
145    {
146      this.entryDN = "";
147    }
148    else
149    {
150      this.entryDN = entryDN;
151    }
152
153    if (maxResponseTime > 0L)
154    {
155      this.maxResponseTime = maxResponseTime;
156    }
157    else
158    {
159      this.maxResponseTime = DEFAULT_MAX_RESPONSE_TIME;
160    }
161
162    searchRequest = new SearchRequest(this.entryDN, SearchScope.BASE,
163         Filter.createPresenceFilter("objectClass"), "1.1");
164    searchRequest.setResponseTimeoutMillis(this.maxResponseTime);
165  }
166
167
168
169  /**
170   * {@inheritDoc}
171   */
172  @Override()
173  public void ensureNewConnectionValid(final LDAPConnection connection)
174         throws LDAPException
175  {
176    if (invokeOnCreate)
177    {
178      getEntry(connection);
179    }
180  }
181
182
183
184  /**
185   * {@inheritDoc}
186   */
187  @Override()
188  public void ensureConnectionValidForCheckout(final LDAPConnection connection)
189         throws LDAPException
190  {
191    if (invokeOnCheckout)
192    {
193      getEntry(connection);
194    }
195  }
196
197
198
199  /**
200   * {@inheritDoc}
201   */
202  @Override()
203  public void ensureConnectionValidForRelease(final LDAPConnection connection)
204         throws LDAPException
205  {
206    if (invokeOnRelease)
207    {
208      getEntry(connection);
209    }
210  }
211
212
213
214  /**
215   * {@inheritDoc}
216   */
217  @Override()
218  public void ensureConnectionValidForContinuedUse(
219                   final LDAPConnection connection)
220         throws LDAPException
221  {
222    if (invokeForBackgroundChecks)
223    {
224      getEntry(connection);
225    }
226  }
227
228
229
230  /**
231   * {@inheritDoc}
232   */
233  @Override()
234  public void ensureConnectionValidAfterException(
235                   final LDAPConnection connection,
236                   final LDAPException exception)
237         throws LDAPException
238  {
239    super.ensureConnectionValidAfterException(connection, exception);
240
241    if (invokeOnException)
242    {
243      getEntry(connection);
244    }
245  }
246
247
248
249  /**
250   * Retrieves the DN of the entry that will be retrieved when performing the
251   * health checks.
252   *
253   * @return  The DN of the entry that will be retrieved when performing the
254   *          health checks.
255   */
256  public String getEntryDN()
257  {
258    return entryDN;
259  }
260
261
262
263  /**
264   * Retrieves the maximum length of time in milliseconds that this health
265   * check should wait for the entry to be returned.
266   *
267   * @return  The maximum length of time in milliseconds that this health check
268   *          should wait for the entry to be returned.
269   */
270  public long getMaxResponseTimeMillis()
271  {
272    return maxResponseTime;
273  }
274
275
276
277  /**
278   * Indicates whether this health check will test for the existence of the
279   * target entry whenever a new connection is created.
280   *
281   * @return  {@code true} if this health check will test for the existence of
282   *          the target entry whenever a new connection is created, or
283   *          {@code false} if not.
284   */
285  public boolean invokeOnCreate()
286  {
287    return invokeOnCreate;
288  }
289
290
291
292  /**
293   * Indicates whether this health check will test for the existence of the
294   * target entry whenever a connection is to be checked out for use.
295   *
296   * @return  {@code true} if this health check will test for the existence of
297   *          the target entry whenever a connection is to be checked out, or
298   *          {@code false} if not.
299   */
300  public boolean invokeOnCheckout()
301  {
302    return invokeOnCheckout;
303  }
304
305
306
307  /**
308   * Indicates whether this health check will test for the existence of the
309   * target entry whenever a connection is to be released back to the pool.
310   *
311   * @return  {@code true} if this health check will test for the existence of
312   *          the target entry whenever a connection is to be released, or
313   *          {@code false} if not.
314   */
315  public boolean invokeOnRelease()
316  {
317    return invokeOnRelease;
318  }
319
320
321
322  /**
323   * Indicates whether this health check will test for the existence of the
324   * target entry during periodic background health checks.
325   *
326   * @return  {@code true} if this health check will test for the existence of
327   *          the target entry during periodic background health checks, or
328   *          {@code false} if not.
329   */
330  public boolean invokeForBackgroundChecks()
331  {
332    return invokeForBackgroundChecks;
333  }
334
335
336
337  /**
338   * Indicates whether this health check will test for the existence of the
339   * target entry if an exception is caught while processing an operation on a
340   * connection.
341   *
342   * @return  {@code true} if this health check will test for the existence of
343   *          the target entry whenever an exception is caught, or {@code false}
344   *          if not.
345   */
346  public boolean invokeOnException()
347  {
348    return invokeOnException;
349  }
350
351
352
353  /**
354   * Attempts to retrieve the target entry.  If the attempt fails, or if the
355   * connection takes too long then an exception will be thrown.
356   *
357   * @param  conn  The connection to be checked.
358   *
359   * @throws  LDAPException  If a problem occurs while trying to retrieve the
360   *                         entry, or if it cannot be retrieved in an
361   *                         acceptable length of time.
362   */
363  private void getEntry(final LDAPConnection conn)
364          throws LDAPException
365  {
366    try
367    {
368      final SearchResult result = conn.search(searchRequest.duplicate());
369      if (result.getEntryCount() != 1)
370      {
371        throw new LDAPException(ResultCode.NO_RESULTS_RETURNED,
372             ERR_GET_ENTRY_HEALTH_CHECK_NO_ENTRY_RETURNED.get());
373      }
374    }
375    catch (Exception e)
376    {
377      debugException(e);
378
379      final String msg = ERR_GET_ENTRY_HEALTH_CHECK_FAILURE.get(entryDN,
380           getExceptionMessage(e));
381
382      conn.setDisconnectInfo(DisconnectType.POOLED_CONNECTION_DEFUNCT, msg, e);
383      throw new LDAPException(ResultCode.SERVER_DOWN, msg, e);
384    }
385  }
386
387
388
389  /**
390   * {@inheritDoc}
391   */
392  @Override()
393  public void toString(final StringBuilder buffer)
394  {
395    buffer.append("GetEntryLDAPConnectionPoolHealthCheck(entryDN='");
396    buffer.append(entryDN);
397    buffer.append("', maxResponseTimeMillis=");
398    buffer.append(maxResponseTime);
399    buffer.append(", invokeOnCreate=");
400    buffer.append(invokeOnCreate);
401    buffer.append(", invokeOnCheckout=");
402    buffer.append(invokeOnCheckout);
403    buffer.append(", invokeOnRelease=");
404    buffer.append(invokeOnRelease);
405    buffer.append(", invokeForBackgroundChecks=");
406    buffer.append(invokeForBackgroundChecks);
407    buffer.append(", invokeOnException=");
408    buffer.append(invokeOnException);
409    buffer.append(')');
410  }
411}