org.bdware.doip.cluster.client.DoipClusterClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of doip-audit-tool Show documentation
Show all versions of doip-audit-tool Show documentation
doip audit tool developed by bdware
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);
}
}