001/*
002 * Copyright 2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 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) 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.util.concurrent.TimeUnit;
041import java.util.concurrent.atomic.LongAdder;
042
043import com.unboundid.util.NotMutable;
044import com.unboundid.util.NotNull;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047import com.unboundid.util.Validator;
048
049import static com.unboundid.ldap.sdk.LDAPMessages.*;
050
051
052
053/**
054 * This class provides an implementation of an LDAP connection pool health check
055 * that will cause the associated connection pool to consider a connection
056 * invalid after it has remained idle (as determined using the
057 * {@link LDAPConnection#getLastCommunicationTime()} method) for more than a
058 * specified length of time.  This is primarily useful in cases where the
059 * associated directory servers (or some intermediate networking equipment) may
060 * terminate connections that have remained idle for too long.
061 * <BR><BR>
062 * Note that in connection pools that may contain connections across multiple
063 * servers, you should probably use the
064 * {@link LDAPConnectionPool#setMaxConnectionAgeMillis(long)} method instead of
065 * this health check to ensure that connections are automatically refreshed
066 * after a specified duration, regardless of whether they have been idle.
067 * Setting a maximum connection age will help ensure that connections in the
068 * pool will return to a relatively balanced state after a failure has caused
069 * connections to migrate away from one or more of those servers.
070 * <BR><BR>
071 * Also note that as an alternative to this health check, you may wish to
072 * consider a health check that actually attempts to communicate with the
073 * destination server over LDAP (e.g., the
074 * {@link GetEntryLDAPConnectionPoolHealthCheck}).  Not only will those types of
075 * health checks do a better job of ensuring that the connection is still valid
076 * (and that the server to which it is established is responsive), but the
077 * communication that they perform will also prevent them from being considered
078 * idle.
079 *
080 * @see  LDAPConnectionPool
081 * @see  LDAPConnectionPoolHealthCheck
082 */
083@NotMutable()
084@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
085public final class MaximumIdleDurationLDAPConnectionPoolHealthCheck
086       extends LDAPConnectionPoolHealthCheck
087{
088  // A counter that will be incremented any time an idle connection is
089  // identified.
090  @NotNull private final LongAdder idleConnectionCounter;
091
092  // The maximum length of time in milliseconds that connections will be allowed
093  // to remain idle before they will be replaced by the associated connection
094  // pool.
095  private final long maximumIdleDurationMillis;
096
097
098
099  /**
100   * Creates a new instance of this health check that will use the specified
101   * maximum idle duration.
102   *
103   * @param  maximumIdleDurationValue
104   *              The value that specifies the maximum length of time, in
105   *              conjunction with the specified time unit, that connections
106   *              will be allowed to remain idle before they will be replaced
107   *              by the associated connection pool.  This value must be greater
108   *              than zero.
109   * @param  maximumIdleDurationTimeUnit
110   *              The time unit to use when interpreting the provided maximum
111   *              idle duration value.  It must not be {@code null}.
112   */
113  public MaximumIdleDurationLDAPConnectionPoolHealthCheck(
114       final long maximumIdleDurationValue,
115       @NotNull final TimeUnit maximumIdleDurationTimeUnit)
116  {
117    this(maximumIdleDurationTimeUnit.toMillis(maximumIdleDurationValue));
118  }
119
120
121
122  /**
123   * Creates a new instance of this health check that will use the specified
124   * maximum idle duration.
125   *
126   * @param  maximumIdleDurationMillis
127   *              The maximum length of time in milliseconds that connections
128   *              will be allowed to remain idle before they will be replaced by
129   *              the associated connection pool.  This value must be greater
130   *              than zero.
131   */
132  public MaximumIdleDurationLDAPConnectionPoolHealthCheck(
133              final long maximumIdleDurationMillis)
134  {
135    Validator.ensureTrue((maximumIdleDurationMillis > 0L),
136         "MaximumIdleDurationLDAPConnectionPoolHealthCheck." +
137              "maximumIdleDurationMillis must be greater than zero.");
138
139    this.maximumIdleDurationMillis = maximumIdleDurationMillis;
140
141    idleConnectionCounter = new LongAdder();
142  }
143
144
145
146  /**
147   * Retrieves the maximum length of time in milliseconds that connections will
148   * be allowed to remain idle before they will be replaced by the associated
149   * connection pool.
150   *
151   * @return  The maximum length of time in milliseconds that connections will
152   *          be allowed to remain idle before they will be replaced by the
153   *          associated connection pool.
154   */
155  public long getMaximumIdleDurationMillis()
156  {
157    return maximumIdleDurationMillis;
158  }
159
160
161
162  /**
163   * Retrieves the number of pooled connections that this health check has
164   * considered invalid because of their idle duration.
165   *
166   * @return  The number of pooled connections that this health check has
167   *          considered invalid because of their idle duration.
168   */
169  public long getIdleConnectionCount()
170  {
171    return idleConnectionCounter.longValue();
172  }
173
174
175
176  /**
177   * {@inheritDoc}
178   */
179  @Override()
180  public void ensureConnectionValidForContinuedUse(
181                   @NotNull final LDAPConnection connection)
182         throws LDAPException
183  {
184    final long currentTime = System.currentTimeMillis();
185    final long lastCommunicationTime = connection.getLastCommunicationTime();
186    final long idleDurationMillis = currentTime - lastCommunicationTime;
187
188    if (idleDurationMillis > maximumIdleDurationMillis)
189    {
190      idleConnectionCounter.increment();
191      throw new LDAPException(ResultCode.TIMEOUT,
192           ERR_IDLE_HEALTH_CHECK_CONNECTION_IDLE.get(idleDurationMillis,
193                maximumIdleDurationMillis));
194    }
195  }
196
197
198
199  /**
200   * Appends a string representation of this LDAP connection pool health check
201   * to the provided buffer.
202   *
203   * @param  buffer  The buffer to which the information should be appended.
204   */
205  public void toString(@NotNull final StringBuilder buffer)
206  {
207    buffer.append("MaximumIdleDurationLDAPConnectionPoolHealthCheck(" +
208         "maximumIdleDurationMillis=");
209    buffer.append(maximumIdleDurationMillis);
210    buffer.append(')');
211  }
212}