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}