package com.bazaarvoice.ostrich.pool; import com.bazaarvoice.ostrich.PartitionContext; import com.bazaarvoice.ostrich.RetryPolicy; import com.bazaarvoice.ostrich.ServiceCallback; import com.bazaarvoice.ostrich.ServicePool; import com.bazaarvoice.ostrich.exceptions.ServiceException; import com.google.common.base.Throwables; import com.google.common.reflect.AbstractInvocationHandler; import java.io.Closeable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; class ServicePoolProxy<S> extends AbstractInvocationHandler { private final Class<S> _serviceType; private final RetryPolicy _retryPolicy; private final ServicePool<S> _servicePool; private final PartitionContextSupplier _partitionContextSupplier; private final boolean _shutdownPoolOnClose; static <S> S create(Class<S> serviceType, RetryPolicy retryPolicy, ServicePool<S> pool, PartitionContextSupplier partitionContextSupplier, boolean shutdownPoolOnClose) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?>[] interfaces = shutdownPoolOnClose ? new Class<?>[] {serviceType, Closeable.class} : new Class<?>[] {serviceType}; ServicePoolProxy<S> proxy = new ServicePoolProxy<S>( serviceType, retryPolicy, pool, partitionContextSupplier, shutdownPoolOnClose); return serviceType.cast(Proxy.newProxyInstance(loader, interfaces, proxy)); } ServicePoolProxy(Class<S> serviceType, RetryPolicy retryPolicy, ServicePool<S> servicePool, PartitionContextSupplier partitionContextSupplier, boolean shutdownPoolOnClose) { checkState(serviceType.isInterface(), "Proxy functionality is only available for interface service types."); _serviceType = checkNotNull(serviceType); _retryPolicy = checkNotNull(retryPolicy); _servicePool = checkNotNull(servicePool); _partitionContextSupplier = checkNotNull(partitionContextSupplier); _shutdownPoolOnClose = shutdownPoolOnClose; } /** * @return The service pool used by this proxy to execute service methods. */ com.bazaarvoice.ostrich.ServicePool<S> getServicePool() { return _servicePool; } @Override protected Object handleInvocation(Object proxy, final Method method, final Object[] args) throws Throwable { // Special case for close() allows closing the entire pool by calling close() on the proxy. if (_shutdownPoolOnClose && args.length == 0 && method.getName().equals("close")) { _servicePool.close(); return null; } PartitionContext partitionContext = _partitionContextSupplier.forCall(method, args); // Delegate the method through to a service provider in the pool. return _servicePool.execute(partitionContext, _retryPolicy, new ServiceCallback<S, Object>() { @Override public Object call(S service) throws ServiceException { try { return method.invoke(service, args); } catch (IllegalAccessException e) { throw Throwables.propagate(e); } catch (InvocationTargetException e) { throw Throwables.propagate(e.getTargetException()); } } }); } @Override public String toString() { return "ServicePoolProxy[" + _serviceType.getName() + "]"; } }