001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.io.Serializable;
026
027 import com.unboundid.util.NotMutable;
028 import com.unboundid.util.ThreadSafety;
029 import com.unboundid.util.ThreadSafetyLevel;
030
031 import static com.unboundid.ldap.sdk.LDAPMessages.*;
032 import static com.unboundid.util.Debug.*;
033 import 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)
053 public 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 }