ConnectionPool.java
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.jdbc.pool.jmx;
import java.util.Properties;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.apache.tomcat.jdbc.pool.PoolProperties.InterceptorDefinition;
import org.apache.tomcat.jdbc.pool.PoolUtilities;
import org.apache.tomcat.jdbc.pool.Validator;
import org.apache.tomcat.jdbc.pool.util.MessageFactory;
/**
* JMX MBean wrapper for a {@link org.apache.tomcat.jdbc.pool.ConnectionPool}. Provides management and monitoring
* capabilities for connection pool statistics, operations, and configuration.
*/
public class ConnectionPool extends NotificationBroadcasterSupport implements ConnectionPoolMBean, MBeanRegistration {
private static final Log log = LogFactory.getLog(ConnectionPool.class);
/**
* the connection pool
*/
protected org.apache.tomcat.jdbc.pool.ConnectionPool pool = null;
/**
* sequence for JMX notifications
*/
protected AtomicInteger sequence = new AtomicInteger(0);
/**
* Listeners that are local and interested in our notifications, no need for JMX
*/
protected ConcurrentLinkedQueue<NotificationListener> listeners = new ConcurrentLinkedQueue<>();
/**
* How many delivery tasks the notifier thread accepts into its queue before further notifications are dropped and
* logged rather than queued. The queue bounds the memory a flooded event source - the pool notifying once per
* failed borrow while it is exhausted - can make the pool allocate; it is not a promise that every notification is
* delivered.
*/
private static final int NOTIFY_QUEUE_CAPACITY = 2000;
/**
* The single thread that pool notifications are delivered on. Delivery goes through here rather than running on the
* thread that reports the condition, because several of the pool's notifications are raised from the connection
* lifecycle - some of them while holding the lock of the connection concerned - and a monitoring listener that is
* slow or blocked must not stall those paths. One worker keeps the notifications in the order the pool raised them;
* the bounded queue and the drop on overflow keep a listener that stops draining from growing the queue without
* limit. The thread is a daemon, is started on the first notification and dies when it has been idle, so a pool
* that never notifies costs nothing, and it never holds up the JVM on shutdown. Shared by every pool MBean in the
* JVM: notifications of one pool can queue behind those of another, but a blocked listener then only starves
* delivery - the drop above is what a pool's operations feel, not the block.
*/
private static final ThreadPoolExecutor NOTIFIER = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(NOTIFY_QUEUE_CAPACITY), r -> {
Thread t = new Thread(r, "JDBC-Pool-JMX-Notifier");
t.setDaemon(true);
return t;
});
/**
* the ObjectName of this pool, or {@code null} while this MBean is not registered - before its registration and
* after its deregistration. Written by the threads that register and deregister the MBean and read by the pool's
* own threads, so it is volatile - as it already is on the {@link org.apache.tomcat.jdbc.pool.DataSource} that
* publishes this MBean - to make those writes visible to the threads that raise notifications.
*/
private volatile ObjectName oname = null;
/**
* Creates a new JMX wrapper for the given connection pool.
*
* @param pool the connection pool to wrap
*/
public ConnectionPool(org.apache.tomcat.jdbc.pool.ConnectionPool pool) {
// Run the per-listener hand-offs that sendNotification builds on the
// thread that calls sendNotification instead of queueing one task per
// listener: notify() hands a notification to the notifier as a single
// task and calls sendNotification on that thread, so a queued
// hand-off would in the end only ever run on the very thread calling
// it - and a delivery queue that stayed full could refuse some of
// those re-queued tasks after the queue had accepted the notification
// as a whole, splitting one notification across its own listeners.
// Run inline, the bounded queue sees one task per notification and
// accepts or refuses the notification as a whole - see
// NOTIFY_QUEUE_CAPACITY and notify(String,String)
super(command -> command.run());
this.pool = pool;
}
/**
* Returns the underlying connection pool instance.
*
* @return the wrapped connection pool
*/
public org.apache.tomcat.jdbc.pool.ConnectionPool getPool() {
return pool;
}
/**
* Returns the pool configuration for the wrapped connection pool.
*
* @return the pool configuration properties
*/
public PoolConfiguration getPoolProperties() {
return pool.getPoolProperties();
}
/**
* Returns the JMX ObjectName under which this MBean is registered.
*
* @return the JMX ObjectName, or {@code null} while this MBean is not registered - before its registration and
* after its deregistration
*/
public ObjectName getObjectName() {
return oname;
}
@Override
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception {
this.oname = name;
return name;
}
@Override
public void postRegister(Boolean registrationDone) {
}
@Override
public void preDeregister() throws Exception {
}
/**
* Drops the registration name.
* <p>
* The MBean server invokes this when the MBean is deregistered, and the pool it wraps may well keep running - a
* container that takes its DataSource back releases the pool MBean without closing the pool. From then on the name
* resolves to no MBean: notifications the pool still raises must not attribute themselves to it (see
* {@link #getNotificationSource()}, which falls back to naming the pool as a string), and no further MBean may be
* published below it. {@link #preRegister(MBeanServer, ObjectName)} writes the name again if this MBean is ever
* registered anew.
*/
@Override
public void postDeregister() {
this.oname = null;
}
// The notification types this MBean sends are declared as constants on
// the ConnectionPoolMBean interface.
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
MBeanNotificationInfo[] pres = super.getNotificationInfo();
MBeanNotificationInfo[] loc = getDefaultNotificationInfo();
MBeanNotificationInfo[] aug = new MBeanNotificationInfo[pres.length + loc.length];
if (pres.length > 0) {
System.arraycopy(pres, 0, aug, 0, pres.length);
}
if (loc.length > 0) {
System.arraycopy(loc, 0, aug, pres.length, loc.length);
}
return aug;
}
/**
* Returns the default notification info describing all notification types that this MBean can send.
*
* @return array of MBeanNotificationInfo describing supported notifications
*/
public static MBeanNotificationInfo[] getDefaultNotificationInfo() {
String[] types = new String[] { NOTIFY_INIT, NOTIFY_CONNECT, NOTIFY_ABANDON, SLOW_QUERY_NOTIFICATION,
FAILED_QUERY_NOTIFICATION, SUSPECT_ABANDONED_NOTIFICATION, POOL_EMPTY, SUSPECT_RETURNED_NOTIFICATION };
String name = Notification.class.getName();
String description = "A connection pool error condition was met.";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description);
return new MBeanNotificationInfo[] { info };
}
/**
* Reports a condition to the pool's notification listeners.
* <p>
* The delivery is asynchronous: the notification is handed to the pool's notification thread as one task, and that
* task delivers it - first through {@link #sendNotification(Notification)} to the listeners reached through JMX,
* then to the ones registered locally with {@link #addListener(NotificationListener)}. Nothing of the delivery runs
* on the thread that calls this method, not even the JMX listeners' filters: the pool raises several of its
* notifications from the connection lifecycle, some of them while holding the lock of the connection concerned,
* and a monitoring listener - or a filter - that is slow or blocked must not reach those paths. The notification
* itself is still built on the calling thread, so it carries the time, sequence number and serializable source of
* the condition it reports - see {@link #getNotificationSource()} - and, since the notifier is a single thread and
* delivers a notification to all its listeners in one go, every listener observes the notifications in the order
* the pool reported them and a listener registered both through JMX and locally is reached through JMX before it
* is reached locally, as it was when delivery was synchronous.
*
* @param type The notification type
* @param message The message
*
* @return true if the notification was accepted for delivery as a whole, false if it was dropped as a whole -
* which happens when the delivery queue is full or the notification could not be built. A false means
* nothing was scheduled: no listener will receive this notification. The result otherwise reports the
* handoff only: it says nothing about whether any listener was reached or what a listener did with
* the notification.
*/
public boolean notify(final String type, String message) {
try {
Notification n = new Notification(type, getNotificationSource(), sequence.incrementAndGet(),
System.currentTimeMillis(), "[" + type + "] " + message);
// Hand the notification over as one task: the delivery queue is
// then the single gate that accepts or refuses it, so the result
// and the drop log below stay true to what was scheduled - a
// refusal never means the notification partly reached its
// listeners, it means nothing was queued at all. On the notifier
// thread the JMX listeners are reached first - the constructor
// runs their hand-offs inline, on this thread - and the local
// ones next, so a listener registered on both sides is reached
// through JMX before it is reached locally, as it was when
// delivery was synchronous.
NOTIFIER.execute(() -> {
try {
sendNotification(n);
} catch (Exception x) {
// sendNotification contains what a listener or a filter
// throws, job per job; this catch is a defence against a
// failure escaping it, which must not skip the local
// listeners of this notification
if (log.isDebugEnabled()) {
log.debug("JMX notification hand-off failed. Type=" + type, x);
}
}
deliverLocally(n, type);
});
return true;
} catch (RejectedExecutionException x) {
// The delivery queue is full; dropping the notification is the
// point of the bound, and must not fail the pool operation that
// reported the condition
log.warn(MessageFactory.get("jmxConnectionPool.droppingNotification",
type, getNotificationSource(), NOTIFY_QUEUE_CAPACITY));
return false;
} catch (Exception x) {
if (log.isDebugEnabled()) {
log.debug("Notify failed. Type=" + type + "; Message=" + message, x);
}
return false;
}
}
/**
* Hands a notification to the listeners registered with this MBean outside JMX. Runs on the notifier thread. Each
* listener is handed the notification on its own and what one of them throws is contained at that one listener: it
* may not kill the notifier thread that every pool in the JVM delivers its notifications on, and it may not
* suppress the delivery of that notification to the listeners registered behind the thrower either. An
* {@link Error} is not contained - as it is not on the JMX half of the delivery, where the JDK broadcaster catches
* only what a listener throws - and takes the notifier thread down; the next notification starts a new one.
*
* @param n the notification to deliver
* @param type the notification type, for the failure log
*/
private void deliverLocally(Notification n, String type) {
for (NotificationListener listener : listeners) {
try {
listener.handleNotification(n, this);
} catch (Exception x) {
// Contained at the listener that threw: the notification
// still reaches the listeners behind it and the notifier
// thread goes on to the next notification. An Error is left
// alone, as on the JMX half of the delivery - the JDK
// broadcaster's hand-off catches only a listener's exceptions
// - and takes this thread down, to be replaced when the next
// notification arrives
if (log.isDebugEnabled()) {
log.debug("Local notification listener failed. Type=" + type, x);
}
}
}
}
/**
* Returns the source the pool's notifications are sent with.
* <p>
* {@link Notification} marshals its source, so a listener outside this JVM - one reached through a JMX connector,
* or one that hands the notification on to another process - only receives the notification at all if the source is
* serializable. This MBean is not, and neither is the pool it wraps, so the source is the {@link ObjectName} the
* MBean is registered under, which names the pool and is serializable. A pool MBean that is not registered - one
* that has never been registered, or one the server has taken back while its pool keeps running - has no such
* name: the source is then a string of the shape the name would have had.
*
* @return the registered ObjectName, or a string naming the pool if the MBean is not registered
*/
protected Object getNotificationSource() {
// The registration handle is written by whichever thread registers or
// deregisters the MBean; the field is volatile so those writes become
// visible to the pool threads that read it here, and it is read once so
// that a single notification is sent with one source whatever happens
// to the field while the notification is on its way
ObjectName name = oname;
if (name != null) {
return name;
}
return org.apache.tomcat.jdbc.pool.ConnectionPool.POOL_JMX_TYPE_PREFIX + "ConnectionPool,name=" + getPoolName();
}
/**
* Adds a local notification listener that receives pool notifications without requiring JMX registration.
*
* @param list the notification listener to add
*/
public void addListener(NotificationListener list) {
listeners.add(list);
}
/**
* Removes a local notification listener from the pool.
*
* @param list the notification listener to remove
*
* @return {@code true} if the listener was removed
*/
public boolean removeListener(NotificationListener list) {
return listeners.remove(list);
}
@Override
public int getSize() {
return pool.getSize();
}
@Override
public int getIdle() {
return pool.getIdle();
}
@Override
public int getActive() {
return pool.getActive();
}
@Override
public int getNumIdle() {
return getIdle();
}
@Override
public int getNumActive() {
return getActive();
}
@Override
public int getWaitCount() {
return pool.getWaitCount();
}
@Override
public long getBorrowedCount() {
return pool.getBorrowedCount();
}
@Override
public long getReturnedCount() {
return pool.getReturnedCount();
}
@Override
public long getCreatedCount() {
return pool.getCreatedCount();
}
@Override
public long getReleasedCount() {
return pool.getReleasedCount();
}
@Override
public long getReconnectedCount() {
return pool.getReconnectedCount();
}
@Override
public long getRemoveAbandonedCount() {
return pool.getRemoveAbandonedCount();
}
@Override
public long getReleasedIdleCount() {
return pool.getReleasedIdleCount();
}
@Override
public void checkIdle() {
pool.checkIdle();
}
@Override
public void checkAbandoned() {
pool.checkAbandoned();
}
@Override
public void testIdle() {
pool.testAllIdle();
}
@Override
public void resetStats() {
pool.resetStats();
}
@Override
public String getConnectionProperties() {
return getPoolProperties().getConnectionProperties();
}
@Override
public Properties getDbProperties() {
return PoolUtilities.cloneWithoutPassword(getPoolProperties().getDbProperties());
}
@Override
public String getDefaultCatalog() {
return getPoolProperties().getDefaultCatalog();
}
@Override
public int getDefaultTransactionIsolation() {
return getPoolProperties().getDefaultTransactionIsolation();
}
@Override
public String getDriverClassName() {
return getPoolProperties().getDriverClassName();
}
@Override
public int getInitialSize() {
return getPoolProperties().getInitialSize();
}
@Override
public String getInitSQL() {
return getPoolProperties().getInitSQL();
}
@Override
public String getJdbcInterceptors() {
return getPoolProperties().getJdbcInterceptors();
}
@Override
public int getMaxActive() {
return getPoolProperties().getMaxActive();
}
@Override
public int getMaxIdle() {
return getPoolProperties().getMaxIdle();
}
@Override
public int getMaxWait() {
return getPoolProperties().getMaxWait();
}
@Override
public int getMinEvictableIdleTimeMillis() {
return getPoolProperties().getMinEvictableIdleTimeMillis();
}
@Override
public int getMinIdle() {
return getPoolProperties().getMinIdle();
}
@Override
public long getMaxAge() {
return getPoolProperties().getMaxAge();
}
@Override
public String getName() {
return this.getPoolName();
}
@Override
public int getNumTestsPerEvictionRun() {
return getPoolProperties().getNumTestsPerEvictionRun();
}
/**
* @return DOES NOT RETURN THE PASSWORD, IT WOULD SHOW UP IN JMX
*/
@Override
public String getPassword() {
return "Password not available as DataSource/JMX operation.";
}
@Override
public int getRemoveAbandonedTimeout() {
return getPoolProperties().getRemoveAbandonedTimeout();
}
@Override
public int getTimeBetweenEvictionRunsMillis() {
return getPoolProperties().getTimeBetweenEvictionRunsMillis();
}
@Override
public String getUrl() {
return getPoolProperties().getUrl();
}
@Override
public String getUsername() {
return getPoolProperties().getUsername();
}
@Override
public long getValidationInterval() {
return getPoolProperties().getValidationInterval();
}
@Override
public String getValidationQuery() {
return getPoolProperties().getValidationQuery();
}
@Override
public int getValidationQueryTimeout() {
return getPoolProperties().getValidationQueryTimeout();
}
@Override
public String getValidatorClassName() {
return getPoolProperties().getValidatorClassName();
}
@Override
public Validator getValidator() {
return getPoolProperties().getValidator();
}
@Override
public boolean isAccessToUnderlyingConnectionAllowed() {
return getPoolProperties().isAccessToUnderlyingConnectionAllowed();
}
@Override
public Boolean isDefaultAutoCommit() {
return getPoolProperties().isDefaultAutoCommit();
}
@Override
public Boolean isDefaultReadOnly() {
return getPoolProperties().isDefaultReadOnly();
}
@Override
public boolean isLogAbandoned() {
return getPoolProperties().isLogAbandoned();
}
@Override
public boolean isPoolSweeperEnabled() {
return getPoolProperties().isPoolSweeperEnabled();
}
@Override
public boolean isRemoveAbandoned() {
return getPoolProperties().isRemoveAbandoned();
}
@Override
public int getAbandonWhenPercentageFull() {
return getPoolProperties().getAbandonWhenPercentageFull();
}
@Override
public boolean isTestOnBorrow() {
return getPoolProperties().isTestOnBorrow();
}
@Override
public boolean isTestOnConnect() {
return getPoolProperties().isTestOnConnect();
}
@Override
public boolean isTestOnReturn() {
return getPoolProperties().isTestOnReturn();
}
@Override
public boolean isTestWhileIdle() {
return getPoolProperties().isTestWhileIdle();
}
@Override
public Boolean getDefaultAutoCommit() {
return getPoolProperties().getDefaultAutoCommit();
}
@Override
public Boolean getDefaultReadOnly() {
return getPoolProperties().getDefaultReadOnly();
}
@Override
public InterceptorDefinition[] getJdbcInterceptorsAsArray() {
return getPoolProperties().getJdbcInterceptorsAsArray();
}
@Override
public boolean getUseLock() {
return getPoolProperties().getUseLock();
}
@Override
public boolean isFairQueue() {
return getPoolProperties().isFairQueue();
}
@Override
public boolean isJmxEnabled() {
return getPoolProperties().isJmxEnabled();
}
@Override
public boolean isUseEquals() {
return getPoolProperties().isUseEquals();
}
@Override
public void setAbandonWhenPercentageFull(int percentage) {
getPoolProperties().setAbandonWhenPercentageFull(percentage);
}
@Override
public void setAccessToUnderlyingConnectionAllowed(boolean accessToUnderlyingConnectionAllowed) {
getPoolProperties().setAccessToUnderlyingConnectionAllowed(accessToUnderlyingConnectionAllowed);
}
@Override
public void setDbProperties(Properties dbProperties) {
getPoolProperties().setDbProperties(dbProperties);
}
@Override
public void setDefaultReadOnly(Boolean defaultReadOnly) {
getPoolProperties().setDefaultReadOnly(defaultReadOnly);
}
@Override
public void setMaxAge(long maxAge) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setMaxAge(maxAge);
// Make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setName(String name) {
getPoolProperties().setName(name);
}
@Override
public String getPoolName() {
return getPoolProperties().getName();
}
@Override
public void setConnectionProperties(String connectionProperties) {
getPoolProperties().setConnectionProperties(connectionProperties);
}
@Override
public void setDefaultAutoCommit(Boolean defaultAutoCommit) {
getPoolProperties().setDefaultAutoCommit(defaultAutoCommit);
}
@Override
public void setDefaultCatalog(String defaultCatalog) {
getPoolProperties().setDefaultCatalog(defaultCatalog);
}
@Override
public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
getPoolProperties().setDefaultTransactionIsolation(defaultTransactionIsolation);
}
@Override
public void setDriverClassName(String driverClassName) {
getPoolProperties().setDriverClassName(driverClassName);
}
@Override
public void setFairQueue(boolean fairQueue) {
// No-op - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setInitialSize(int initialSize) {
// No-op - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setInitSQL(String initSQL) {
getPoolProperties().setInitSQL(initSQL);
}
@Override
public void setJdbcInterceptors(String jdbcInterceptors) {
// No-op - this pool is already running
throw new UnsupportedOperationException();
}
@Override
public void setJmxEnabled(boolean jmxEnabled) {
// No-op - this pool is already running and obviously jmx enabled
throw new UnsupportedOperationException();
}
@Override
public void setLogAbandoned(boolean logAbandoned) {
getPoolProperties().setLogAbandoned(logAbandoned);
}
@Override
public void setMaxActive(int maxActive) {
getPoolProperties().setMaxActive(maxActive);
// Make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setMaxIdle(int maxIdle) {
getPoolProperties().setMaxIdle(maxIdle);
// Make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setMaxWait(int maxWait) {
getPoolProperties().setMaxWait(maxWait);
}
@Override
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setMinIdle(int minIdle) {
getPoolProperties().setMinIdle(minIdle);
// Make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
}
@Override
public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
getPoolProperties().setNumTestsPerEvictionRun(numTestsPerEvictionRun);
}
@Override
public void setPassword(String password) {
getPoolProperties().setPassword(password);
}
@Override
public void setRemoveAbandoned(boolean removeAbandoned) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setRemoveAbandoned(removeAbandoned);
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setRemoveAbandonedTimeout(removeAbandonedTimeout);
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setTestOnBorrow(boolean testOnBorrow) {
getPoolProperties().setTestOnBorrow(testOnBorrow);
}
@Override
public void setTestOnConnect(boolean testOnConnect) {
getPoolProperties().setTestOnConnect(testOnConnect);
}
@Override
public void setTestOnReturn(boolean testOnReturn) {
getPoolProperties().setTestOnReturn(testOnReturn);
}
@Override
public void setTestWhileIdle(boolean testWhileIdle) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setTestWhileIdle(testWhileIdle);
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// Make sure the pool is properly configured
pool.checkPoolConfiguration(getPoolProperties());
poolCleanerAttributeUpdated(wasEnabled);
}
/**
* Starts/stops pool cleaner thread as necessary after its configuration properties were updated. This method must
* be called <b>after</b> configuration properties affecting the pool cleaner have been updated.
*
* @param wasEnabled whether the pool cleaner was enabled <b>before</b> the configuration change occurred.
*/
private void poolCleanerAttributeUpdated(boolean wasEnabled) {
boolean shouldBeEnabled = getPoolProperties().isPoolSweeperEnabled();
// Make sure pool cleaner starts/stops when it should
if (!wasEnabled && shouldBeEnabled) {
pool.initializePoolCleaner(getPoolProperties());
} else if (wasEnabled) {
pool.terminatePoolCleaner();
if (shouldBeEnabled) {
pool.initializePoolCleaner(getPoolProperties());
}
}
}
@Override
public void setUrl(String url) {
getPoolProperties().setUrl(url);
}
@Override
public void setUseEquals(boolean useEquals) {
getPoolProperties().setUseEquals(useEquals);
}
@Override
public void setUseLock(boolean useLock) {
getPoolProperties().setUseLock(useLock);
}
@Override
public void setUsername(String username) {
getPoolProperties().setUsername(username);
}
@Override
public void setValidationInterval(long validationInterval) {
getPoolProperties().setValidationInterval(validationInterval);
}
@Override
public void setValidationQuery(String validationQuery) {
getPoolProperties().setValidationQuery(validationQuery);
}
@Override
public void setValidationQueryTimeout(int validationQueryTimeout) {
getPoolProperties().setValidationQueryTimeout(validationQueryTimeout);
}
@Override
public void setValidatorClassName(String className) {
getPoolProperties().setValidatorClassName(className);
}
@Override
public int getSuspectTimeout() {
return getPoolProperties().getSuspectTimeout();
}
@Override
public void setSuspectTimeout(int seconds) {
boolean wasEnabled = getPoolProperties().isPoolSweeperEnabled();
getPoolProperties().setSuspectTimeout(seconds);
poolCleanerAttributeUpdated(wasEnabled);
}
@Override
public void setDataSource(Object ds) {
getPoolProperties().setDataSource(ds);
}
@Override
public Object getDataSource() {
return getPoolProperties().getDataSource();
}
@Override
public void setDataSourceJNDI(String jndiDS) {
getPoolProperties().setDataSourceJNDI(jndiDS);
}
@Override
public String getDataSourceJNDI() {
return getPoolProperties().getDataSourceJNDI();
}
@Override
public boolean isAlternateUsernameAllowed() {
return getPoolProperties().isAlternateUsernameAllowed();
}
@Override
public void setAlternateUsernameAllowed(boolean alternateUsernameAllowed) {
getPoolProperties().setAlternateUsernameAllowed(alternateUsernameAllowed);
}
@Override
public void setValidator(Validator validator) {
getPoolProperties().setValidator(validator);
}
@Override
public void setCommitOnReturn(boolean commitOnReturn) {
getPoolProperties().setCommitOnReturn(commitOnReturn);
}
@Override
public boolean getCommitOnReturn() {
return getPoolProperties().getCommitOnReturn();
}
@Override
public void setRollbackOnReturn(boolean rollbackOnReturn) {
getPoolProperties().setRollbackOnReturn(rollbackOnReturn);
}
@Override
public boolean getRollbackOnReturn() {
return getPoolProperties().getRollbackOnReturn();
}
/**
* {@inheritDoc}
*
* @deprecated The disposable connection facade is always used, so this property no longer has any effect.
*/
@Override
@Deprecated
public void setUseDisposableConnectionFacade(boolean useDisposableConnectionFacade) {
getPoolProperties().setUseDisposableConnectionFacade(useDisposableConnectionFacade);
}
/**
* {@inheritDoc}
*
* @deprecated The disposable connection facade is always used, so this property no longer has any effect.
*/
@Override
@Deprecated
public boolean getUseDisposableConnectionFacade() {
return getPoolProperties().getUseDisposableConnectionFacade();
}
@Override
public void setLogValidationErrors(boolean logValidationErrors) {
getPoolProperties().setLogValidationErrors(logValidationErrors);
}
@Override
public boolean getLogValidationErrors() {
return getPoolProperties().getLogValidationErrors();
}
@Override
public boolean getPropagateInterruptState() {
return getPoolProperties().getPropagateInterruptState();
}
@Override
public void setPropagateInterruptState(boolean propagateInterruptState) {
getPoolProperties().setPropagateInterruptState(propagateInterruptState);
}
@Override
public boolean isIgnoreExceptionOnPreLoad() {
return getPoolProperties().isIgnoreExceptionOnPreLoad();
}
@Override
public void setIgnoreExceptionOnPreLoad(boolean ignoreExceptionOnPreLoad) {
// No-op - this pool is already running
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*
* @deprecated The statement facade is always used, so this property no longer has any effect.
*/
@Override
@Deprecated
public boolean getUseStatementFacade() {
return getPoolProperties().getUseStatementFacade();
}
/**
* {@inheritDoc}
*
* @deprecated The statement facade is always used, so this property no longer has any effect.
*/
@Override
@Deprecated
public void setUseStatementFacade(boolean useStatementFacade) {
getPoolProperties().setUseStatementFacade(useStatementFacade);
}
@Override
public void purge() {
pool.purge();
}
@Override
public void purgeOnReturn() {
pool.purgeOnReturn();
}
}