/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.internal.connection; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import org.mule.runtime.api.config.PoolingProfile; import org.mule.runtime.api.connection.ConnectionException; import org.mule.runtime.api.connection.ConnectionHandler; import org.mule.runtime.api.connection.ConnectionProvider; import org.mule.runtime.api.connection.ConnectionValidationResult; import org.mule.runtime.api.connection.PoolingListener; import org.mule.runtime.core.api.DefaultMuleException; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.api.exception.MuleException; import java.util.NoSuchElementException; import org.apache.commons.pool.ObjectPool; import org.apache.commons.pool.PoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A {@link ConnectionManagementStrategy} which returns connections obtained from a {@link #pool} * * @param <C> the generic type of the connections to be managed * @since 4.0 */ final class PoolingConnectionManagementStrategy<C> extends ConnectionManagementStrategy<C> { private static final Logger LOGGER = LoggerFactory.getLogger(PoolingConnectionManagementStrategy.class); private static final String NULL_VALIDATION_RESULT_ERROR_MESSAGE = "Error validating connection. ConnectionValidationResult can not be null"; private final PoolingProfile poolingProfile; private final ObjectPool<C> pool; private final PoolingListener<C> poolingListener; /** * Creates a new instance * * @param connectionProvider the {@link ConnectionProvider} used to manage the connections * @param poolingProfile the {@link PoolingProfile} which configures the {@link #pool} * @param poolingListener a {@link PoolingListener} * @param muleContext the application's {@link MuleContext} */ PoolingConnectionManagementStrategy(ConnectionProvider<C> connectionProvider, PoolingProfile poolingProfile, PoolingListener<C> poolingListener, MuleContext muleContext) { super(connectionProvider, muleContext); this.poolingProfile = poolingProfile; this.poolingListener = poolingListener; pool = createPool(); } /** * Returns a {@link ConnectionHandler} which wraps a connection obtained from the {@link #pool} * * @return a {@link ConnectionHandler} * @throws ConnectionException if the connection could not be obtained */ @Override public ConnectionHandler<C> getConnectionHandler() throws ConnectionException { try { C connection = borrowConnection(); ConnectionValidationResult validationResult = connectionProvider.validate(connection); if (validationResult == null) { LOGGER.debug(NULL_VALIDATION_RESULT_ERROR_MESSAGE); pool.invalidateObject(connection); throw new ConnectionException(NULL_VALIDATION_RESULT_ERROR_MESSAGE); } else if (!validationResult.isValid()) { pool.invalidateObject(connection); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Error validating connection: {}. Invalidating connection.", validationResult.getMessage()); } throw new ConnectionException(validationResult.getMessage(), validationResult.getException()); } return new PoolingConnectionHandler<>(connection, pool, poolingListener); } catch (ConnectionException e) { throw e; } catch (NoSuchElementException e) { throw new ConnectionException("Connection pool is exhausted"); } catch (Exception e) { throw new ConnectionException("An exception was found trying to obtain a connection", e); } } private C borrowConnection() throws Exception { C connection = pool.borrowObject(); try { poolingListener.onBorrow(connection); } catch (Exception e) { pool.invalidateObject(connection); throw e; } return connection; } /** * Closes the pool, causing the contained connections to be closed as well. * * @throws MuleException */ // TODO: MULE-9082 - pool.close() doesn't destroy unreturned connections @Override public void close() throws MuleException { try { pool.close(); } catch (Exception e) { throw new DefaultMuleException(createStaticMessage("Could not close connection pool"), e); } } private ObjectPool<C> createPool() { GenericObjectPool.Config config = new GenericObjectPool.Config(); config.maxIdle = poolingProfile.getMaxIdle(); config.maxActive = poolingProfile.getMaxActive(); config.maxWait = poolingProfile.getMaxWait(); config.whenExhaustedAction = (byte) poolingProfile.getExhaustedAction(); config.minEvictableIdleTimeMillis = poolingProfile.getMinEvictionMillis(); config.timeBetweenEvictionRunsMillis = poolingProfile.getEvictionCheckIntervalMillis(); GenericObjectPool genericPool = new GenericObjectPool(new ObjectFactoryAdapter(), config); return genericPool; } public PoolingProfile getPoolingProfile() { return poolingProfile; } private class ObjectFactoryAdapter implements PoolableObjectFactory<C> { @Override public C makeObject() throws Exception { return connectionProvider.connect(); } @Override public void destroyObject(C connection) throws Exception { connectionProvider.disconnect(connection); } @Override public boolean validateObject(C obj) { return false; } @Override public void activateObject(C connection) throws Exception {} @Override public void passivateObject(C connection) throws Exception {} } }