001/*
002 * Copyright 2018-2020 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2018-2020 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) 2018-2020 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.atomic.AtomicReference;
041import java.util.logging.Level;
042
043import com.unboundid.util.Debug;
044import com.unboundid.util.DebugType;
045import com.unboundid.util.ThreadSafety;
046import com.unboundid.util.ThreadSafetyLevel;
047
048
049
050/**
051 * This class provides an implementation of an LDAP connection pool health check
052 * that periodically monitors the number of available connections in the pool.
053 * If the number of available connections has been consistently greater than a
054 * specified minimum for at least a given length of time, then the number of
055 * available connections will be reduced to that minimum.  Note that the
056 * size of the pool will only be checked at interval's specified by the
057 * {@link AbstractConnectionPool#getHealthCheckIntervalMillis()} method, so it
058 * is possible that the number of available connections may have dipped below
059 * that minimum on one or more occasions between checks.  Also note that this
060 * health check can only be used on instances of the
061 * {@link LDAPConnectionPool} class; it cannot be used with
062 * {@link LDAPThreadLocalConnectionPool} instances.
063 */
064@ThreadSafety(level= ThreadSafetyLevel.COMPLETELY_THREADSAFE)
065public final class PruneUnneededConnectionsLDAPConnectionPoolHealthCheck
066       extends LDAPConnectionPoolHealthCheck
067{
068  // A reference to the first time at which the number of available connections
069  // exceeded the minimum number of available connections.  It may reference a
070  // null value if the last check indicated that the number of available
071  // connections was not larger than the configured minimum.
072  private final AtomicReference<Long>
073                     earliestTimeWithMoreThanMinAvailableConnections;
074
075  // The minimum number of connections that should be maintained in the
076  // connection pool.  This health check will only remove connections if the
077  // pool has more than this number of connections for at least the specified
078  // duration.
079  private final int minAvailableConnections;
080
081  // The minimum length of time in milliseconds that the pool should have had
082  // at least the specified minimum number of available connections before any
083  // connections may be removed.
084  private final long minDurationMillisExceedingMinAvailableConnections;
085
086
087
088  /**
089   * Creates a new instance of this LDAP connection pool health check with the
090   * provided information.
091   *
092   * @param  minAvailableConnections
093   *              The minimum number of connections that should be maintained in
094   *              the connection pool.  This health check will only remove
095   *              connections if the pool has more than this number of
096   *              connections for at least the specified duration.  A value that
097   *              is less than or equal to zero indicates that no minimum number
098   *              of connections needs to be maintained.
099   * @param  minDurationMillisExceedingMinAvailableConnections
100   *              The minimum length of time in milliseconds that the pool
101   *              should have reported at least the specified minimum number of
102   *              available connections before any connections may be removed.
103   *              Note that the number of connections will only be checked at
104   *              intervals specified by the
105   *              {@link AbstractConnectionPool#getHealthCheckIntervalMillis()}
106   *              method, so it may be possible for the number of available
107   *              connections to dip below this value one or more time between
108   *              intervals and still cause the pool to be reduced in size.  A
109   *              value that is less than or equal to zero indicates that the
110   *              pool size should be reduced to the configured minimum any time
111   *              there are more than that number of connections available.
112   */
113  public PruneUnneededConnectionsLDAPConnectionPoolHealthCheck(
114              final int minAvailableConnections,
115              final long minDurationMillisExceedingMinAvailableConnections)
116  {
117    this.minAvailableConnections = Math.max(0, minAvailableConnections);
118    this.minDurationMillisExceedingMinAvailableConnections = Math.max(0L,
119         minDurationMillisExceedingMinAvailableConnections);
120
121    earliestTimeWithMoreThanMinAvailableConnections = new AtomicReference<>();
122  }
123
124
125
126  /**
127   * Retrieves the minimum number of connections that should be maintained in
128   * the connection pool.  This health check will only remove connections if the
129   * pool has more than this number of connections for at least the specified
130   * duration.
131   *
132   * @return  The minimum number of connections that should be maintained in the
133   *          connection pool.
134   */
135  public int getMinAvailableConnections()
136  {
137    return minAvailableConnections;
138  }
139
140
141
142  /**
143   * Retrieves the minimum length of time in milliseconds that the pool should
144   * have reported at least the specified minimum number of available
145   * connections before any connections may be removed.  Note that the number of
146   * connections will only be checked at intervals specified by the
147   * {@link AbstractConnectionPool#getHealthCheckIntervalMillis()} method, so it
148   * may be possible for the number of available connections to dip below this
149   * value one or more time between intervals and still cause the pool to be
150   * reduced in size.
151   *
152   * @return  The minimum length of time in milliseconds that the pool should
153   *          have reported at least the specified minimum number of available
154   *          connections before any connections may be removed.
155   */
156  public long getMinDurationMillisExceedingMinAvailableConnections()
157  {
158    return minDurationMillisExceedingMinAvailableConnections;
159  }
160
161
162
163  /**
164   * {@inheritDoc}
165   */
166  @Override()
167  public void performPoolMaintenance(final AbstractConnectionPool pool)
168  {
169    if (! (pool instanceof LDAPConnectionPool))
170    {
171      Debug.debug(Level.WARNING, DebugType.CONNECT,
172             "Only " + LDAPConnectionPool.class.getName() +
173                  " instances may be used in conjunction with the " +
174                  "PruneUnneededConnectionsLDAPConnectionPoolHealthCheck.  " +
175                  "The provided pool had an incompatible type of " +
176                  pool.getClass().getName() + '.');
177
178      earliestTimeWithMoreThanMinAvailableConnections.set(null);
179      return;
180    }
181
182    final int availableConnections = pool.getCurrentAvailableConnections();
183    if (availableConnections <= minAvailableConnections)
184    {
185      earliestTimeWithMoreThanMinAvailableConnections.set(null);
186      return;
187    }
188
189    final Long earliestTime =
190         earliestTimeWithMoreThanMinAvailableConnections.get();
191    if (earliestTime == null)
192    {
193      if (minDurationMillisExceedingMinAvailableConnections <= 0L)
194      {
195        ((LDAPConnectionPool) pool).shrinkPool(minAvailableConnections);
196      }
197      else
198      {
199        earliestTimeWithMoreThanMinAvailableConnections.set(
200             System.currentTimeMillis());
201      }
202    }
203    else
204    {
205      final long millisWithMoreThanMinAvailableConnections =
206           System.currentTimeMillis() - earliestTime;
207      if (millisWithMoreThanMinAvailableConnections >=
208           minDurationMillisExceedingMinAvailableConnections)
209      {
210        ((LDAPConnectionPool) pool).shrinkPool(minAvailableConnections);
211        earliestTimeWithMoreThanMinAvailableConnections.set(null);
212      }
213    }
214  }
215
216
217
218  /**
219   * {@inheritDoc}
220   */
221  @Override()
222  public void toString(final StringBuilder buffer)
223  {
224    buffer.append("PruneUnneededConnectionsLDAPConnectionPoolHealthCheck(" +
225         "minAvailableConnections=");
226    buffer.append(minAvailableConnections);
227    buffer.append(", minDurationMillisExceedingMinAvailableConnections=");
228    buffer.append(minDurationMillisExceedingMinAvailableConnections);
229    buffer.append(')');
230  }
231}