/** * */ package com.github.phantomthief.thrift.client.impl; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; import org.apache.thrift.TServiceClient; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TMultiplexedProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.transport.TTransport; import com.github.phantomthief.thrift.client.ThriftClient; import com.github.phantomthief.thrift.client.exception.NoBackendException; import com.github.phantomthief.thrift.client.pool.ThriftConnectionPoolProvider; import com.github.phantomthief.thrift.client.pool.ThriftServerInfo; import com.github.phantomthief.thrift.client.pool.impl.DefaultThriftConnectionPoolImpl; import com.github.phantomthief.thrift.client.utils.ThriftClientUtils; import javassist.util.proxy.Proxy; import javassist.util.proxy.ProxyFactory; /** * <p> * ThriftClientImpl class. * </p> * * @author w.vela * @version $Id: $Id */ public class ThriftClientImpl implements ThriftClient { private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(getClass()); private final ThriftConnectionPoolProvider poolProvider; private final Supplier<List<ThriftServerInfo>> serverInfoProvider; /** * <p> * Constructor for ThriftClientImpl. * </p> * * @param serverInfoProvider provide service list */ public ThriftClientImpl(Supplier<List<ThriftServerInfo>> serverInfoProvider) { this(serverInfoProvider, DefaultThriftConnectionPoolImpl.getInstance()); } /** * <p> * Constructor for ThriftClientImpl. * </p> * * @param serverInfoProvider provide service list * @param poolProvider provide a pool */ public ThriftClientImpl(Supplier<List<ThriftServerInfo>> serverInfoProvider, ThriftConnectionPoolProvider poolProvider) { this.poolProvider = poolProvider; this.serverInfoProvider = serverInfoProvider; } /** * {@inheritDoc} * * <p> * iface. * </p> */ @Override public <X extends TServiceClient> X iface(Class<X> ifaceClass) { return iface(ifaceClass, ThriftClientUtils.randomNextInt()); } /** * {@inheritDoc} * * <p> * iface. * </p> */ @Override public <X extends TServiceClient> X iface(Class<X> ifaceClass, int hash) { return iface(ifaceClass, TCompactProtocol::new, hash); } /** * {@inheritDoc} * * <p> * iface. * </p> */ @SuppressWarnings("unchecked") @Override public <X extends TServiceClient> X iface(Class<X> ifaceClass, Function<TTransport, TProtocol> protocolProvider, int hash) { List<ThriftServerInfo> servers = serverInfoProvider.get(); if (servers == null || servers.isEmpty()) { throw new NoBackendException(); } hash = Math.abs(hash); hash = hash < 0 ? 0 : hash; ThriftServerInfo selected = servers.get(hash % servers.size()); logger.trace("get connection for [{}]->{} with hash:{}", ifaceClass, selected, hash); TTransport transport = poolProvider.getConnection(selected); TProtocol protocol = protocolProvider.apply(transport); ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(ifaceClass); factory.setFilter(m -> ThriftClientUtils.getInterfaceMethodNames(ifaceClass).contains( m.getName())); try { X x = (X) factory.create(new Class[] { org.apache.thrift.protocol.TProtocol.class }, new Object[] { protocol }); ((Proxy) x).setHandler((self, thisMethod, proceed, args) -> { boolean success = false; try { Object result = proceed.invoke(self, args); success = true; return result; } finally { if (success) { poolProvider.returnConnection(selected, transport); } else { poolProvider.returnBrokenConnection(selected, transport); } } }); return x; } catch (NoSuchMethodException | IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("fail to create proxy.", e); } } /** * {@inheritDoc} * * <p> * mpiface. * </p> */ @Override public <X extends TServiceClient> X mpiface(Class<X> ifaceClass, String serviceName) { return mpiface(ifaceClass, serviceName, ThriftClientUtils.randomNextInt()); } /** * {@inheritDoc} * * <p> * mpiface. * </p> */ @Override public <X extends TServiceClient> X mpiface(Class<X> ifaceClass, String serviceName, int hash) { return mpiface(ifaceClass, serviceName, TCompactProtocol::new, hash); } /** * {@inheritDoc} * * <p> * mpiface. * </p> */ @Override public <X extends TServiceClient> X mpiface(Class<X> ifaceClass, String serviceName, Function<TTransport, TProtocol> protocolProvider, int hash) { return iface(ifaceClass, protocolProvider.andThen((p) -> new TMultiplexedProtocol(p, serviceName)), hash); } }