All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dongliu.prettypb.rpc.client.StubServiceFactory Maven / Gradle / Ivy

There is a newer version: 0.3.5
Show newest version
package net.dongliu.prettypb.rpc.client;

import net.dongliu.prettypb.rpc.RpcClient;
import net.dongliu.prettypb.rpc.exception.ServiceException;
import net.dongliu.prettypb.rpc.protocol.RpcRequest;
import net.dongliu.prettypb.rpc.common.MethodInfo;
import net.dongliu.prettypb.rpc.common.ServiceInfo;
import net.dongliu.prettypb.runtime.ProtoBufEncoder;
import net.dongliu.prettypb.runtime.include.RpcCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * proto rpc client service factory.
 *
 * @author Dong Liu
 */
public class StubServiceFactory {

    private Logger logger = LoggerFactory.getLogger(StubServiceFactory.class);

    private final AtomicInteger correlationId;
    private final RpcClient rpcClient;

    public StubServiceFactory(AtomicInteger correlationId, RpcClient rpcClient) {
        this.correlationId = correlationId;
        this.rpcClient = rpcClient;
    }

    @SuppressWarnings("unchecked")
    public  T getService(Class interfaceClass, int timeout) {
        if (timeout <= 0) {
            throw new IllegalArgumentException("timeout should be larger than 0");
        }
        InvocationHandler handler = new RpcInvocationHandler(interfaceClass, timeout);
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class[]{interfaceClass}, handler);
    }

    /**
     * generate service impl for protobuf rpc client
     */
    class RpcInvocationHandler implements InvocationHandler {

        private ServiceInfo serviceInfo;

        /**
         * time out for this service
         */
        private int timeout;

        public RpcInvocationHandler(Class interfaceClass, int timeout) {
            this.serviceInfo = ServiceInfo.inspect(interfaceClass);
            this.serviceInfo.setAllowTimeout(true);
            this.timeout = timeout;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws ServiceException {
            MethodInfo methodInfo = this.serviceInfo.getMethodInfo(method.getName());
            if (methodInfo == null) {
                throw new ServiceException("Method not found:" + this.serviceInfo.getName() + "#"
                        + method.getName());
            }

            RpcRequest rpcRequest = new RpcRequest();
            rpcRequest.setMethodIdentifier(methodInfo.getName());
            rpcRequest.setCorrelationId(correlationId.getAndIncrement());
            rpcRequest.setServicePackage(serviceInfo.getPackageName());
            rpcRequest.setServiceIdentifier(serviceInfo.getName());
            rpcRequest.setTimeoutMs(timeout);

            Class[] parameterTypes = method.getParameterTypes();
            rpcRequest.setRequestBytes(ProtoBufEncoder.toBytes(args[0],
                    (Class) parameterTypes[0]));

            if (serviceInfo.isAsync()) {
                ClientCallTask task = new ClientCallTask(rpcRequest.getCorrelationId(), methodInfo,
                        (RpcCallback) args[1], timeout);
                rpcClient.writeRequest(task, rpcRequest);
                return null;
            } else {
                BlockingRpcCallback callback = new BlockingRpcCallback();
                ClientCallTask task = new ClientCallTask(rpcRequest.getCorrelationId(), methodInfo,
                        callback, timeout);
                rpcClient.writeRequest(task, rpcRequest);
                try {
                    callback.await(timeout);
                } catch (TimeoutException e) {
                    logger.warn("block rpc call timeout, service:{}, name:{}",
                            serviceInfo.getName(), methodInfo.getName());
                    throw new ServiceException(e);
                } catch (InterruptedException e) {
                    logger.warn("block rpc call interrupted, service:{}, name:{}",
                            serviceInfo.getName(), methodInfo.getName());
                    Thread.currentThread().interrupt();
                    throw new ServiceException(e);
                }

                if (callback.getException() != null) {
                    throw new ServiceException(callback.getException());
                }
                return callback.getValue();
            }
        }
    }
}