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

org.bdware.doip.cluster.client.DoipClusterClient Maven / Gradle / Ivy

There is a newer version: 1.5.4
Show newest version
package org.bdware.doip.cluster.client;

import com.google.gson.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.doip.audit.EndpointConfig;
import org.bdware.doip.audit.client.AuditIrpClient;
import org.bdware.doip.cluster.util.ResultCollector;
import org.bdware.doip.codec.digitalObject.DigitalObject;
import org.bdware.doip.codec.doipMessage.DoipMessage;
import org.bdware.doip.codec.doipMessage.DoipMessageSigner;
import org.bdware.doip.codec.metadata.SearchParameter;
import org.bdware.doip.codec.operations.BasicOperations;
import org.bdware.doip.encrypt.SM2Signer;
import org.bdware.doip.endpoint.client.ClientConfig;
import org.bdware.doip.endpoint.client.DoipClientImpl;
import org.bdware.doip.endpoint.client.DoipMessageCallback;
import org.bdware.irp.exception.IrpClientException;
import org.bdware.irp.stateinfo.StateInfoBase;
import org.bdware.sc.bean.JoinInfo;
import org.bdware.sc.bean.RouteInfo;
import org.bdware.sc.util.JsonUtil;
import org.zz.gmhelper.SM2KeyPair;
import org.zz.gmhelper.SM2Util;
import wrp.jdk.nashorn.api.scripting.NashornScriptEngineUtil;

import javax.script.ScriptException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


//Client for single bdo running in cluster model.
//Leverage resolved result as input.
//connections between different bdo is reused if possible.
public class DoipClusterClient extends DoipClientImpl {
    private DoipMessageSigner doipMessageSigner;
    private AuditIrpClient repoIrpClient;
    private EndpointConfig routerConfig;
    private String[] members;
    // all the doipOperation and routeInfo binding relationships
    private Map doipOperationToRouteInfo;
    // all the doipOperation and joinInfo binding relationships
    private Map doipOperationToJoinInfo;
    // doipConnectMap是和类绑定的,这样不同的DoipClusterClient对象,才能够复用同一个连接池
    static Map doipConnectMap = new HashMap<>();
    static Logger Logger = LogManager.getLogger(DoipClusterClient.class);

    private NashornScriptEngineUtil engineUtil;

    public DoipClusterClient(EndpointConfig routerConfig) throws IrpClientException, ScriptException {
        initClientVariables(routerConfig);
        initClientFromRouter();
        Logger.info("The DoipClusterClient has been initialized");
    }

    public void initClientVariables(EndpointConfig routerConfig) {
        this.routerConfig = routerConfig;
        repoIrpClient = new AuditIrpClient(routerConfig);
        Logger.info("The repoIrpClient initialization has done");
        // initiate doipMessageSigner
        if (routerConfig.publicKey != null && routerConfig.privateKey != null) {
            doipMessageSigner = new SM2Signer(SM2KeyPair.fromJson(new Gson().toJson(routerConfig)));
        } else
            doipMessageSigner = new SM2Signer(SM2Util.generateSM2KeyPair());

        engineUtil = new NashornScriptEngineUtil();
        doipOperationToRouteInfo = new HashMap<>();
        doipOperationToJoinInfo = new HashMap<>();
    }

    public void initClientFromRouter() throws IrpClientException, ScriptException {
        String repoIdentifier = "bdtest/" + routerConfig.repoName;
        StateInfoBase routerInfo = repoIrpClient.resolve(repoIdentifier);
        if(routerInfo == null) {
            Logger.error("InitClientFromRouter failed, the StateInfoBase is empty");
            throw new IrpClientException("InitClientFromRouter failed");
        }
        JsonObject handleValues = routerInfo.handleValues;
        if(handleValues == null || handleValues.isJsonNull()) {
            Logger.error("InitClientFromRouter failed, the HandleValues in StateInfoBase is empty.");
            throw new IrpClientException("InitClientFromRouter failed");

        }
        if(handleValues.get("cluster") == null || handleValues.get("cluster").isJsonNull()) {
            Logger.error("Router's HandleValue doesn't contain cluster info");
            throw new IrpClientException("InitClientFromRouter failed");
        }
        Gson gson = new Gson();
        handleValues = gson.fromJson(handleValues.get("cluster").getAsString(), JsonObject.class);

        resolveClusterInfo(handleValues);
        initEngine(handleValues);
        resolveRouteJoinInfo(handleValues);
    }

    public void resolveClusterInfo(JsonObject handleValues) {
        JsonElement clusterInfo = handleValues.get("clusterInfo");
        if(clusterInfo == null || clusterInfo.isJsonNull()) return;

        List doipServers = new LinkedList<>();
        if(clusterInfo.isJsonArray()) {
            JsonArray doipServerInfoArray = clusterInfo.getAsJsonArray();
            for (JsonElement doipServer : doipServerInfoArray) {
                doipServers.add(doipServer.getAsString());
            }
        }

        engineUtil.doipClusterUtil.doipServers = new String[doipServers.size()];
        this.members = new String[doipServers.size()];
        for (int i = 0; i < doipServers.size(); i++) {
            engineUtil.doipClusterUtil.doipServers[i] = doipServers.get(i);
            this.members[i] = doipServers.get(i);
        }

    }

    public void initEngine(JsonObject handleValues) throws ScriptException {
        JsonElement functions = handleValues.get("functions");
        if(functions == null || functions.isJsonNull()) return;

        // 将所有的functions通过engine注册到functionBindings中
        if(functions.isJsonObject()) {
            JsonObject functionsObj = functions.getAsJsonObject();
            for (String funcName: functionsObj.keySet()) {
                String funcScript = functionsObj.get(funcName).getAsString();
                engineUtil.evalFunction(funcScript);
            }
        }
    }

    public void resolveRouteJoinInfo(JsonObject handleValues) {
        JsonElement routeInfos = handleValues.get("routeInfo");
        if(routeInfos == null || routeInfos.isJsonNull()) return;

        // maintain doipOperationToRouteInfo
        Gson gson = new Gson();
        if(routeInfos.isJsonObject()) {
            JsonObject routeInfosMap = routeInfos.getAsJsonObject();

            for (String doipOperationName: routeInfosMap.keySet()) {
                String doipFunctionRouteInfoJson = routeInfosMap.get(doipOperationName).getAsString();
                RouteInfo doipFunctionRouteInfo = gson.fromJson(doipFunctionRouteInfoJson, RouteInfo.class);
                doipOperationToRouteInfo.put(doipOperationName, doipFunctionRouteInfo);
            }
        }

        // maintain doipOperationToJoinInfo
        JsonElement joinInfos = handleValues.get("joinInfo");
        if(joinInfos == null || joinInfos.isJsonNull()) return;
        if(joinInfos.isJsonObject()) {
            JsonObject joinInfosMap = joinInfos.getAsJsonObject();

            for (String doipOperationName: joinInfosMap.keySet()) {
                String doipFunctionJoinInfoJson = joinInfosMap.get(doipOperationName).getAsString();
                JoinInfo doipFunctionJoinInfo = gson.fromJson(doipFunctionJoinInfoJson, JoinInfo.class);
                doipOperationToJoinInfo.put(doipOperationName, doipFunctionJoinInfo);
            }
        }
    }

    @Override
    public void hello(String id, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Hello, id, null, null, false, null);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void listOperations(String id, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.ListOps, id, null, null, false, null);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void retrieve(String id, String element, boolean includeElementData, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Retrieve, id, null, element, includeElementData, null);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void create(String targetDOIPServiceID, DigitalObject digitalObject, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Create, targetDOIPServiceID, null, null, false, digitalObject);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void update(DigitalObject digitalObject, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Update, digitalObject.id, null, null, false, digitalObject);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void delete(String id, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Delete, id, null, null, false, null);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    @Override
    public void search(String id, SearchParameter sp, DoipMessageCallback cb) {
        DoipParam doipParam = new DoipParam(BasicOperations.Search, id, sp, null, false, null);
        String[] targetDoipServers = getDoipServersByRouteInfo(doipParam);
        // joinParams都来自于callback,joinParams就是所有节点callback到达之后,merge之后的结果
        callServersAndJoin(doipParam, targetDoipServers, cb);
    }

    public DoipClientImpl getDoipClientForServer(String targetDoipServer) {
        DoipClientImpl doipClientImpl = doipConnectMap.getOrDefault(targetDoipServer, null);
        try {
            if(doipClientImpl == null) {
                doipClientImpl = new DoipClientImpl();
                doipConnectMap.put(targetDoipServer, doipClientImpl);
                doipClientImpl.connect(ClientConfig.fromUrl(targetDoipServer));
            } else {
                doipClientImpl.reconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
            Logger.error(targetDoipServer + " cannot be correctly connected");
        }

        return doipClientImpl;
    }

    // 根据请求参数,执行RouteInfo,寻找DoipServers
    public String[] getDoipServersByRouteInfo(DoipParam doipParam) {
        try {
            JsonObject routeInfoArg = composeRouteInfoArg(doipParam.operation, doipParam.identifier, doipParam.sp, doipParam.element, doipParam.includeElementData, doipParam.digitalObject);
            String operationName = doipParam.operation.getName();

            // If the operation truly has routeInfo
            RouteInfo routeInfo = doipOperationToRouteInfo.getOrDefault(operationName, null);
            if(routeInfo != null) {
                if(routeInfo.useDefault == null) {
                    JsonElement requester = routerConfig.publicKey == null ? JsonNull.INSTANCE : new JsonPrimitive(routerConfig.publicKey);
                    return engineUtil.invokeFunction(routeInfo.funcName, String[].class, routeInfoArg, requester);
                }

                switch (routeInfo.useDefault) {
                    case byRequester:
                        int val = new BigInteger(routerConfig.publicKey, 16).mod(new BigInteger("" + members.length)).intValue();
                        while (val < 0 && members.length > 0) {
                            val = val + members.length;
                        }
                        return new String[]{members[val]};
                    case byArgHash:
                        val = routeInfoArg.hashCode();
                        val = val % members.length;
                        while (val < 0 && members.length > 0) {
                            val += members.length;
                        }
                        return new String[]{members[val]};
                    case byJsonPropHash:
                        JsonElement jo = tryLoadJsonProp(routeInfoArg, routeInfo.param);
                        val = jo.toString().hashCode() % members.length;
                        while (val < 0 && members.length > 0) {
                            val += members.length;
                        }
                        return new String[]{members[val]};
                    default:
                        return members;
                }
            }

            throw new Exception("The function has not routeInfo");
        } catch (Exception e) {
            e.printStackTrace();
            Logger.error("getDoipServersByRouteInfo has something wrong, all servers are returned.");
            return members;
        }
    }

    private JsonElement tryLoadJsonProp(JsonObject routeInfoArg, String param) {
        try {
            if (routeInfoArg == null)
                return JsonNull.INSTANCE;
            JsonObject arg;
            if (routeInfoArg.isJsonPrimitive()) {
                arg = JsonUtil.parseString(routeInfoArg.getAsString()).getAsJsonObject();
            } else arg = routeInfoArg.getAsJsonObject();
            if (!param.contains(".")) {
                return arg.get(param);
            } else {
                String[] props = param.split("\\.");
                JsonElement result = arg;
                for (String str : props)
                    result = result.getAsJsonObject().get(str);
                return result;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return JsonNull.INSTANCE;
        }
    }

    // 根据请求参数,组装RouteInfo的参数Arg
    public JsonObject composeRouteInfoArg(BasicOperations operation, String identifier, SearchParameter sp, String element, boolean includeElementData, DigitalObject digitalObject) {
        JsonObject composedRes = new JsonObject();
        JsonObject headerProperties = new JsonObject();
        headerProperties.addProperty("identifier", identifier);
        switch (operation) {
            case Hello:
            case Delete:
            case ListOps:
                break;
            case Create:
            case Update:
                composedRes.addProperty("body", digitalObject.toString());
                break;
            case Search:
                JsonObject searchAttributes = new JsonObject();
                searchAttributes.addProperty("query", sp.query);
                searchAttributes.addProperty("pageNum", sp.pageNum);
                searchAttributes.addProperty("pageSize", sp.pageSize);
                searchAttributes.addProperty("type", sp.type);
                headerProperties.add("attributes", searchAttributes);
                break;
            case Retrieve:
                JsonObject retrieveAttributes = new JsonObject();
                if(element != null) retrieveAttributes.addProperty("element", element);
                if(includeElementData) retrieveAttributes.addProperty("includeElementData", "true");
                if(!retrieveAttributes.equals(new JsonObject())) headerProperties.add("attributes", retrieveAttributes);
                break;
        }

        composedRes.add("header", headerProperties);
        return composedRes;
    }

    private int getJoinCount(JoinInfo joinInfo, int serversNum, JsonObject joinParams) {
        try {
            if (joinInfo == null) return serversNum;
            if (joinInfo.joinCountFuncName != null) {
                JsonElement requester = routerConfig.publicKey == null ? JsonNull.INSTANCE : new JsonPrimitive(routerConfig.publicKey);
                return engineUtil.invokeFunction(joinInfo.joinCountFuncName, Integer.class, joinParams, requester);
            }
            if (joinInfo.joinCount != 0) return joinInfo.joinCount;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return serversNum;
    }

    public JoinInfo getJoinInfo(BasicOperations operation) {
        return doipOperationToJoinInfo.getOrDefault(operation.getName(), null);
    }

    public void callServersAndJoin(DoipParam doipParam, String[] targetDoipServers, DoipMessageCallback cb) {
        JoinInfo joinInfo = getJoinInfo(doipParam.operation);
        int serversNum = targetDoipServers.length;
        int count = getJoinCount(joinInfo, serversNum, new JsonObject());
        // 如果joinCountFunc执行算出的joinCount,比所有的servers还要多,就更新为servers的数量
        if (serversNum < count) {
            count = serversNum;
        }

        if (count > 0) {
            ResultCollector resultCollector = new ResultCollector(cb, count, engineUtil, joinInfo);

            for (String targetDoipServer : targetDoipServers) {
                DoipClientImpl doipClientImpl = getDoipClientForServer(targetDoipServer);
                switch (doipParam.operation) {
                    case Hello:
                        doipClientImpl.hello(doipParam.identifier ,resultCollector);
                        break;
                    case ListOps:
                        doipClientImpl.listOperations(doipParam.identifier, resultCollector);
                        break;
                    case Retrieve:
                        doipClientImpl.retrieve(doipParam.identifier, doipParam.element, doipParam.includeElementData,resultCollector);
                        break;
                    case Create:
                        doipClientImpl.create(doipParam.identifier, doipParam.digitalObject, resultCollector);
                        break;
                    case Update:
                        doipClientImpl.update(doipParam.digitalObject, resultCollector);
                        break;
                    case Delete:
                        doipClientImpl.delete(doipParam.identifier, resultCollector);
                        break;
                    case Search:
                        doipClientImpl.search(doipParam.identifier, doipParam.sp, resultCollector);
                        break;
                }
            }
        } else {
            DoipMessage errDoipMessage = new DoipMessage(doipParam.identifier, "");
            errDoipMessage.body.encodedData = "count is 0".getBytes(StandardCharsets.UTF_8);
            Logger.warn("count is 0");
            cb.onResult(errDoipMessage);
        }
    }

    @Override
    public void sendMessage(DoipMessage msg, DoipMessageCallback cb) {
        if (doipMessageSigner != null) {
            doipMessageSigner.signMessage(msg);
        }

        super.sendMessage(msg, cb);
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy