/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2024-2026 3A Systems, LLC. */ package org.opends.server.backends.jdbc; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import com.github.benmanes.caffeine.cache.RemovalCause; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import java.sql.*; import java.time.Duration; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CachedConnection implements Connection { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); static final String TTL_PROPERTY = "org.openidentityplatform.opendj.jdbc.ttl"; static final long DEFAULT_TTL_MS = 15000; /** * Bounds the connect and the login of one attempt to establish a connection, in seconds; 0 for * no bound of its own - the deadline of {@value #POOL_TIMEOUT_PROPERTY} still bounds the * attempt, since it stands for the whole borrow. Setting both to 0 is what leaves a connect * unbounded. */ static final String CONNECT_TIMEOUT_PROPERTY = "org.openidentityplatform.opendj.jdbc.connect.timeout"; static final long DEFAULT_CONNECT_TIMEOUT_SECONDS = 30; /** * Bounds a whole borrow - every connect attempt and every wait for a pooled connection - in * seconds; 0 for no bound. Not to the millisecond: the connection in hand is validated * whatever the deadline says, and an attempt is never given less than a second, so a borrow * can return a validation and a last attempt past it. */ static final String POOL_TIMEOUT_PROPERTY = "org.openidentityplatform.opendj.jdbc.pool.timeout"; static final long DEFAULT_POOL_TIMEOUT_SECONDS = 60; /** Bound of the validation of a pooled connection: isValid(0) means "no timeout" in the JDBC contract. */ static final int VALIDATION_TIMEOUT_SECONDS = 5; /** 08001, sqlclient_unable_to_establish_sqlconnection: the state of a connect that did not happen. */ private static final String CONNECT_FAILED_SQL_STATE = "08001"; /** 53300, too_many_connections: how the standard - and postgresql - reports a server taking no further connection. */ private static final String CONNECTION_LIMIT_SQL_STATE = "53300"; /** 57P03, cannot_connect_now: postgresql starting up, shutting down or in recovery. */ private static final String NOT_ACCEPTING_YET_SQL_STATE = "57P03"; static final long MAX_BACKOFF_MS = 1000; static final long STALL_WARNING_AFTER_MS = 1000; static final long STALL_WARNING_INTERVAL_MS = 10000; /** How many links of the cause and getNextException() chains of a failure are looked at. */ private static final int MAX_CHAIN_LENGTH = 32; /** What a connection string is cut down to where this cannot tell its credentials from the rest of it. */ static final String CREDENTIALS_HIDDEN = "