package com.lambdaworks.redis; import java.io.Closeable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collections; import java.util.Set; import com.lambdaworks.redis.internal.AbstractInvocationHandler; import com.lambdaworks.redis.support.ConnectionPoolSupport; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; /** * Connection pool for redis connections. * * @author Mark Paluch * @param <T> Connection type. * @since 3.0 * @deprecated Will be removed in future versions. Use {@link ConnectionPoolSupport}. */ @Deprecated public class RedisConnectionPool<T> implements Closeable { private final RedisConnectionProvider<T> redisConnectionProvider; private GenericObjectPool<T> objectPool; private CloseEvents closeEvents = new CloseEvents(); /** * Create a new connection pool * * @param redisConnectionProvider the connection provider * @param maxActive max active connections * @param maxIdle max idle connections * @param maxWait max wait time (ms) for a connection */ public RedisConnectionPool(RedisConnectionProvider<T> redisConnectionProvider, int maxActive, int maxIdle, long maxWait) { this.redisConnectionProvider = redisConnectionProvider; GenericObjectPoolConfig config = new GenericObjectPoolConfig(); config.setMaxIdle(maxIdle); config.setMaxTotal(maxActive); config.setMaxWaitMillis(maxWait); config.setTestOnBorrow(true); objectPool = new GenericObjectPool<T>(createFactory(redisConnectionProvider), config); } private PooledObjectFactory<T> createFactory(final RedisConnectionProvider<T> redisConnectionProvider) { return new BasePooledObjectFactory<T>() { @SuppressWarnings("unchecked") @Override public T create() throws Exception { T connection = redisConnectionProvider.createConnection(); PooledConnectionInvocationHandler<T> h = new PooledConnectionInvocationHandler<T>(connection, RedisConnectionPool.this); Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { redisConnectionProvider.getComponentType() }, h); return (T) proxy; } @Override public PooledObject<T> wrap(T obj) { return new DefaultPooledObject<T>(obj); } @Override public boolean validateObject(PooledObject<T> p) { return Connections.isOpen(p.getObject()); } @Override @SuppressWarnings("unchecked") public void destroyObject(PooledObject<T> p) throws Exception { T object = p.getObject(); if (Proxy.isProxyClass(object.getClass())) { PooledConnectionInvocationHandler<T> invocationHandler = (PooledConnectionInvocationHandler<T>) Proxy .getInvocationHandler(object); object = invocationHandler.getConnection(); } Connections.close(object); } }; } /** * Allocate a connection from the pool. It must be returned using freeConnection (or alternatively call {@code close()} on * the connection). * * The connections returned by this method are proxies to the underlying connections. * * @return a pooled connection. */ public T allocateConnection() { try { return objectPool.borrowObject(); } catch (RedisException e) { throw e; } catch (Exception e) { throw new RedisException(e.getMessage(), e); } } /** * Return a connection into the pool. * * @param t the connection. */ public void freeConnection(T t) { objectPool.returnObject(t); } /** * * @return the number of idle connections */ public int getNumIdle() { return objectPool.getNumIdle(); } /** * * @return the number of active connections. */ public int getNumActive() { return objectPool.getNumActive(); } /** * Close the pool and close all idle connections. Active connections won't be closed. */ @Override public void close() { objectPool.close(); objectPool = null; closeEvents.fireEventClosed(this); closeEvents = null; } /** * * @return the component type (pool resource type). */ public Class<? extends T> getComponentType() { return redisConnectionProvider.getComponentType(); } /** * Adds a CloseListener. * * @param listener the listener */ void addListener(CloseEvents.CloseListener listener) { closeEvents.addListener(listener); } /** * Invocation handler which takes care of connection.close(). Connections are returned to the pool on a close()-call. * * @author Mark Paluch * @param <T> Connection type. * @since 3.0 */ static class PooledConnectionInvocationHandler<T> extends AbstractInvocationHandler { public static final Set<String> DISABLED_METHODS = Collections.singleton("getStatefulConnection"); private T connection; private final RedisConnectionPool<T> pool; public PooledConnectionInvocationHandler(T connection, RedisConnectionPool<T> pool) { this.connection = connection; this.pool = pool; } @SuppressWarnings("unchecked") @Override protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable { if (DISABLED_METHODS.contains(method.getName())) { throw new UnsupportedOperationException( "Calls to " + method.getName() + " are not supported on pooled connections"); } if (connection == null) { throw new RedisException("Connection is deallocated and cannot be used anymore."); } if (method.getName().equals("close")) { if (pool.objectPool == null) { return method.invoke(connection, args); } pool.freeConnection((T) proxy); return null; } try { return method.invoke(connection, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } public T getConnection() { return connection; } } /** * Connection provider for redis connections. * * @author Mark Paluch * @param <T> Connection type. * @since 3.0 */ interface RedisConnectionProvider<T> { T createConnection(); Class<? extends T> getComponentType(); } }