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