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

cn.hyperchain.sdk.provider.ProviderManager Maven / Gradle / Ivy

There is a newer version: 1.4.3
Show newest version
package cn.hyperchain.sdk.provider;

import cn.hyperchain.sdk.common.utils.Async;
import cn.hyperchain.sdk.common.utils.ByteUtil;
import cn.hyperchain.sdk.common.utils.Utils;
import cn.hyperchain.sdk.crypto.cert.CertKeyPair;
import cn.hyperchain.sdk.exception.AllNodesBadException;
import cn.hyperchain.sdk.exception.RequestException;
import cn.hyperchain.sdk.exception.RequestExceptionCode;
import cn.hyperchain.sdk.grpc.Transaction.CommonReq;
import cn.hyperchain.sdk.grpc.GrpcUtil;
import cn.hyperchain.sdk.request.FileTransferRequest;
import cn.hyperchain.sdk.request.NodeRequest;
import cn.hyperchain.sdk.request.Request;
import cn.hyperchain.sdk.request.TCertRequest;
import cn.hyperchain.sdk.request.SendBatchTxsRequest;
import cn.hyperchain.sdk.response.TCertResponse;
import cn.hyperchain.sdk.response.did.DIDResponse;
import cn.hyperchain.sdk.response.tx.TxVersionResponse;
import cn.hyperchain.sdk.service.DIDService;
import cn.hyperchain.sdk.service.ServiceManager;
import cn.hyperchain.sdk.service.TxService;
import cn.hyperchain.sdk.transaction.TxVersion;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ProviderManager responsible for load balancing, encapsulating headers, etc.
 */
public class ProviderManager {
    private String namespace = "global";
    public String chainID = "";
    private TCertPool tCertPool;
    private List httpProviders;
    private List grpcProviders;
    private List fileMgrHttpProviders;
    private boolean isCFCA;
    private boolean enableGRPC;
    private static Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

    private ProviderManager() {
    }

    private static Logger logger = LogManager.getLogger(ProviderManager.class);

    public static ProviderManager emptyManager() {
        return new ProviderManager();
    }

    public static class Builder {
        private ProviderManager providerManager;

        public Builder() {
            providerManager = new ProviderManager();
        }

        /**
         * set provider manager http providers.
         * @param httpProviders http providers
         * @return {@link Builder}
         */
        public Builder providers(HttpProvider... httpProviders) {
            if (httpProviders == null || httpProviders.length == 0) {
                throw new IllegalStateException("can't initialize a ProviderManager instance with empty HttpProviders");
            }
            providerManager.httpProviders = new ArrayList<>(httpProviders.length);
            providerManager.httpProviders.addAll(Arrays.asList(httpProviders));
            return this;
        }

        /**
         * set provider manager grpc providers.
         *
         * @param grpcProviders grpc providers
         * @return {@link Builder}
         */
        public Builder grpcProviders(GrpcProvider... grpcProviders) {
            if (grpcProviders == null || grpcProviders.length == 0) {
                throw new IllegalStateException("can't initialize a ProviderManager instance with empty HttpProviders");
            }
            providerManager.enableGRPC = true;
            providerManager.grpcProviders = new ArrayList<>(grpcProviders.length);
            providerManager.grpcProviders.addAll(Arrays.asList(grpcProviders));
            return this;
        }

        /**
         * set provider manager fileMgr http providers.
         * @param fileMgrHttpProviders fileMgr http providers
         * @return {@link Builder}
         */
        public Builder fileMgrHttpProviders(FileMgrHttpProvider... fileMgrHttpProviders) {
            if (fileMgrHttpProviders == null || fileMgrHttpProviders.length == 0) {
                throw new IllegalStateException("can't initialize a ProviderManager instance with empty FileMgrHttpProviders");
            }
            providerManager.fileMgrHttpProviders = new ArrayList<>(fileMgrHttpProviders.length);
            providerManager.fileMgrHttpProviders.addAll(Arrays.asList(fileMgrHttpProviders));
            return this;
        }

        /**
         * enable TCert to request http.
         * @param sdkCert sdkCert inputStream
         * @param sdkCertPriv sdkCert private key inputStream
         * @param uniquePub unique public key inputStream
         * @param uniquePriv unique private key inputStream
         * @return {@link Builder}
         */
        public Builder enableTCert(InputStream sdkCert, InputStream sdkCertPriv, InputStream uniquePub, InputStream uniquePriv) {
            if (providerManager.tCertPool != null) {
                logger.warn("warn: TCertPool has been initialized");
            }
            providerManager.isCFCA = false;
            try {
                TCertPool tCertPool = new TCertPool(sdkCert, sdkCertPriv, uniquePub, uniquePriv);
                providerManager.settCertPool(tCertPool);
            } catch (Exception e) {
                throw new RuntimeException("init TCertPool error, cause " + e.getMessage());
            }
            return this;
        }

        /**
         * use cafa to request http.
         * @param sdkCert sdkCert inputStream
         * @param sdkCertPriv sdkCert private key inputStream
         * @return {@link Builder}
         */
        public Builder cfca(InputStream sdkCert, InputStream sdkCertPriv) {
            if (providerManager.tCertPool != null) {
                logger.warn("warn: TCertPool has been initialized");
            }
            providerManager.isCFCA = true;
            try {
                TCertPool tCertPool = new TCertPool(sdkCert, sdkCertPriv);
                providerManager.settCertPool(tCertPool);
            } catch (Exception e) {
                throw new RuntimeException("init TCertPool error, cause " + e.getMessage());
            }
            return this;
        }

        /**
         * set provider manager's namespace.
         * @param namespace namespace
         * @return {@link Builder}
         */
        public Builder namespace(String namespace) {
            providerManager.setNamespace(namespace);
            return this;
        }

        /**
         * return provider manager instance.
         * @return {@link ProviderManager}
         */
        public ProviderManager build() {
            setTxVersion(providerManager);
            providerManager.setLocalChainID();
            return providerManager;
        }
    }

    /**
     * create provider manager by {@link HttpProvider}.
     * @param httpProviders {@link HttpProvider}
     * @return provider manager
     */
    public static ProviderManager createManager(HttpProvider... httpProviders) {
        if (httpProviders == null || httpProviders.length == 0) {
            throw new IllegalStateException("can't initialize a ProviderManager instance with empty HttpProviders");
        }
        ProviderManager providerManager = new ProviderManager();
        if (httpProviders[0] instanceof GrpcProvider) {
            providerManager.grpcProviders = new ArrayList<>();
            providerManager.grpcProviders.addAll(Arrays.asList(httpProviders));
        } else {
            providerManager.httpProviders = new ArrayList<>();
            providerManager.httpProviders.addAll(Arrays.asList(httpProviders));
        }
        setTxVersion(providerManager);
        return providerManager;
    }

    private List checkIds(List httpProviders, int... ids) throws RequestException {
        // use all with null
        if (ids == null || ids.length == 0) {
            return httpProviders;
        }

        List providers = new ArrayList<>();
        for (int id : ids) {
            if (id > 0 && id <= httpProviders.size()) {
                providers.add(httpProviders.get(id - 1));
            } else {
                throw new RequestException(RequestExceptionCode.PARAM_ERROR, "id is ouf of range");
            }
        }
        return providers;
    }

    /**
     * send request to node and get response.
     * @param request request
     * @param ids specific ids
     * @return response string
     * @throws RequestException -
     */
    public String send(Request request, int... ids) throws RequestException {
        List hProviders;
        if (request instanceof FileTransferRequest) {
            hProviders = checkIds(fileMgrHttpProviders, ids);
        } else if (enableGRPC && GrpcUtil.isGRPCMethod(request.getMethod()) && !(request instanceof SendBatchTxsRequest)) {
            request.setGRPC(true);
            hProviders = checkIds(grpcProviders, ids);
        } else {
            if (GrpcUtil.isOnlyGRPCMethod(request.getMethod())) {
                throw new RequestException(RequestExceptionCode.GRPC_SERVICE_WRONG);
            }
            request.setGRPC(false);
            hProviders = checkIds(httpProviders, ids);
        }
        int providerSize = hProviders.size();
        int startIndex = Utils.randInt(1, providerSize);
        boolean flag = true;
        for (int i = 0; i < providerSize; i++) {
            HttpProvider hProvider = hProviders.get((startIndex + i) % providerSize);
            if (i == providerSize - 1 && hProvider instanceof GrpcProvider && hProvider.getStatus() != PStatus.NORMAL && flag) {
                i = -1;
                flag = false;
                continue;
            }
            if (flag ? hProvider.getStatus() == PStatus.NORMAL : hProvider.getStatus() != PStatus.ABNORMAL) {
                logger.debug("[REQUEST] request node id: " + ((startIndex + i) % providerSize + 1));
                try {
                    request.setNamespace(this.namespace);
//                    DefaultHttpProvider dProvider = (DefaultHttpProvider) hProvider;
                    if (hProvider.getAccount() != null) {
                        // use dProvider sign
                        Request.Authentication auth = new Request.Authentication(Utils.genTimestamp(), hProvider.getAccount().getAddress());
                        String needHashString = auth.getNeedHashString();
                        byte[] sourceData = needHashString.getBytes(Utils.DEFAULT_CHARSET);
                        auth.setSignature(ByteUtil.toHex(hProvider.getAccount().sign(sourceData)));
                        request.setAuth(auth);
                    }
                    return sendTo(request, hProvider);
                } catch (RequestException e) {
                    //todo grpc在某些情况下也需要重连(等其他接口服务恢复之后添加)
                    if (e.getCode().equals(RequestExceptionCode.NETWORK_GETBODY_FAILED.getCode())) {
                        logger.debug("send to provider: " + hProvider.getUrl() + " failed");
                        reconnect(hProvider);
                        continue;
                    }
                    // throw other exception
                    throw e;
                }
            }
        }
        logger.error("All nodes are bad, please check it or wait for reconnecting successfully!");
        throw new AllNodesBadException("No node to connect!");
    }

    private String sendTo(Request request, HttpProvider provider) throws RequestException {
        requestCheck(request, provider);
        byte[] bodyBytes;
        if (request.isGRPC()) {
            CommonReq commonReq = GrpcUtil.convertRequestToCommonReq(request);
            bodyBytes = commonReq.toByteArray();
        } else {
            String body = request.requestBody();
            bodyBytes = body.getBytes(Utils.DEFAULT_CHARSET);
        }
        if (this.tCertPool != null) {
            boolean f = this.isCFCA;
            // todo may different txs have different version
            if (TxVersion.GLOBAL_TX_VERSION.isGreaterOrEqual(TxVersion.TxVersion20)) {
                // is flato
                f = true;
            }
            if (f) {
                request.addHeader("tcert", this.tCertPool.getSdkCert());
                request.addHeader("signature", this.tCertPool.getSdkCertKeyPair().signData(bodyBytes));
            } else {
                String tCert = this.tCertPool.getTCert(provider.getUrl());
                if (tCert == null) {
                    tCert = this.getTCert(this.tCertPool.getUniquePubKey(), this.tCertPool.getSdkCertKeyPair(), provider);
                    this.tCertPool.setTCert(provider.getUrl(), tCert);
                }
                request.addHeader("tcert", tCert);
                request.addHeader("signature", this.tCertPool.getUniqueKeyPair().signData(bodyBytes));
            }
        }
        return provider.post(request);
    }

    private String getTCert(String uniquePubKey, CertKeyPair sdkCertKeyPair, HttpProvider provider) throws RequestException {
        TCertRequest tCertRequest = new TCertRequest("cert_getTCert", null, null);
        tCertRequest.setNamespace(this.namespace);
        Map param = new HashMap<>();
        param.put("pubkey", uniquePubKey);
        tCertRequest.addParams(param);
        String body = tCertRequest.requestBody();
        byte[] bodyBytes = body.getBytes(Utils.DEFAULT_CHARSET);
        tCertRequest.addHeader("tcert", sdkCertKeyPair.getPublicKey());
        tCertRequest.addHeader("signature", sdkCertKeyPair.signData(bodyBytes));
        String response = provider.post(tCertRequest);
        TCertResponse tCertResponse = gson.fromJson(response, TCertResponse.class);
        if (tCertResponse.getCode() == RequestExceptionCode.METHOD_NOT_FOUND.getCode()) {
            throw new RequestException(tCertResponse.getCode(), tCertResponse.getMessage());
        }
        return tCertResponse.getTCert();
    }

    private void requestCheck(Request request, HttpProvider provider) throws RequestException {
        boolean isFileMgrRequest = request instanceof FileTransferRequest;
        boolean isFileMgrHttpProvider = provider instanceof FileMgrHttpProvider;
        if (isFileMgrHttpProvider != isFileMgrRequest) {
            throw new RequestException(RequestExceptionCode.REQUEST_TYPE_ERROR);
        }
    }

    private void reconnect(final HttpProvider provider) {
        Request request = new NodeRequest("node_getNodes", null, null);
        Async.run(() -> {
            while (provider.getStatus() == PStatus.ABNORMAL) {
                try {
                    sendTo(request, provider);
                } catch (RequestException e) {
                    if (e.getCode().equals(RequestExceptionCode.NETWORK_PROBLEM.getCode())) {
                        logger.error("reconnect to node " + provider.getUrl() + " failed, will try again...");
                        Thread.sleep(20000L);
                        continue;
                    }
                }
                logger.info("reconnect to node " + provider.getUrl() + " success.");
                provider.setStatus(PStatus.NORMAL);
                return null;
            }
            return null;
        });
    }

    public String getNamespace() {
        return namespace;
    }

    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    public TCertPool gettCertPool() {
        return tCertPool;
    }

    public void settCertPool(TCertPool tCertPool) {
        this.tCertPool = tCertPool;
    }

    public boolean isCFCA() {
        return isCFCA;
    }

    /**
     * set the global TxVersion.
     *
     * @param providerManager specific providerManger
     */
    public static void setTxVersion(ProviderManager providerManager) {
        int nodeNum = providerManager.httpProviders.size();
        TxService txService = ServiceManager.getTxService(providerManager);
        try {
            String txVersionResult = "";
            int count = 0;
            for (int i = 1; i <= nodeNum; i++) {
                if (providerManager.tCertPool != null && !TxVersion.GLOBAL_TX_VERSION.isGreaterOrEqual(TxVersion.TxVersion20) && !providerManager.isCFCA) {
                    try {
                        String tCert = providerManager.getTCert(providerManager.tCertPool.getUniquePubKey(), providerManager.tCertPool.getSdkCertKeyPair(), providerManager.httpProviders.get(i - 1));
                        providerManager.tCertPool.setTCert(providerManager.httpProviders.get(i - 1).getUrl(), tCert);
                    } catch (RequestException e) {
                        if (e.getCode().equals(RequestExceptionCode.METHOD_NOT_FOUND.getCode())) {
                            logger.info(e.getMessage() + ". set txVersion to 2.0.");
                            TxVersion.setGlobalTxVersion("2.0");
                        }
                    }
                }
                TxVersionResponse txVersionResponse = txService.getTxVersion(i).send();
                String txVersion = txVersionResponse.getTxVersionResult();
                if (txVersionResult.equals(txVersion)) {
                    count++;
                } else {
                    txVersionResult = txVersion;
                }
            }
            if (count == nodeNum - 1) {
                TxVersion.setGlobalTxVersion(txVersionResult);
            } else {
                logger.warn("the TxVersion of nodes is different, the platform's TxVersion is " + TxVersion.GLOBAL_TX_VERSION);
            }
        } catch (RequestException e) {
            logger.info(e.toString());
        }
    }

    /**
     * set the global chainID.
     */
    public void setLocalChainID() {
        DIDService didService = ServiceManager.getDIDService(this);
        try {
            if (TxVersion.GLOBAL_TX_VERSION.isGreaterOrEqual(TxVersion.TxVersion26)) {
                DIDResponse didResponse = didService.getChainID().send();
                chainID = didResponse.getResult();
            } else {
                logger.warn(TxVersion.GLOBAL_TX_VERSION + " platform does't support DID service  ");
            }
        } catch (RequestException e) {
            logger.info(e.toString());
        }

    }

    public void setChainID(String chainID) {
        this.chainID = chainID;
    }

    public String getChainID() {
        return chainID;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy