diff --git a/build.gradle b/build.gradle index e7664f3..dbf9341 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,8 @@ plugins { } group = "org.bdware.sc" -version = "1.7.2" +version = "1.8.0" + repositories { mavenCentral() mavenLocal() @@ -59,7 +60,7 @@ jar { } from { // uncomment this when publish, - //while develop at local use "false" + // while develop at local use "false" configurations.runtimeClasspath.filter { // it.getAbsolutePath().contains("/lib/") false diff --git a/src/main/java/org/bdware/sc/ContractProcess.java b/src/main/java/org/bdware/sc/ContractProcess.java index 7bf5b66..51be291 100644 --- a/src/main/java/org/bdware/sc/ContractProcess.java +++ b/src/main/java/org/bdware/sc/ContractProcess.java @@ -32,6 +32,7 @@ import org.bdware.sc.engine.hook.*; import org.bdware.sc.handler.ContractHandler; import org.bdware.sc.index.TimeSerialIndex; import org.bdware.sc.node.*; +import org.bdware.sc.server.DoipClusterServer; import org.bdware.sc.trace.ProgramPointCounter; import org.bdware.sc.util.FileUtil; import org.bdware.sc.util.HashUtil; @@ -517,6 +518,17 @@ public class ContractProcess { jo.add("loadContract", JsonUtil.parseObject(result)); jo.addProperty("status", result.status.merge(onCreate.status).toString()); LOGGER.debug("result: " + jo.toString()); + + // doipModule的话,拉起DoipServer服务端口 + if(cn.getYjsType() == YjsType.DoipModule) { + // 只有一台机器去更新Router中的repoInfo就可以了 + if(JavaScriptEntry.shardingID == 0) { + DoipClusterServer server = DoipClusterServer.getDOOPServerInstance(); + server.updateRepoInfo(contract, cn); + } + invokeOnStartingDoipServer(cn, contract.getCreateParam()); + } + return jo.toString(); } else { contract.setScript(FileUtil.getFileContent(zipPath)); @@ -557,9 +569,11 @@ public class ContractProcess { if (argSchemaHandler != null) { fun.appendBeforeInvokeHandler(argSchemaHandler); } - if (fun.functionName.equals("onCreate")){ - fun.appendAfterInvokeHandler(new ObjToJsonHandler()); + + if (fun.isDoipOperation()) { + fun.appendBeforeInvokeHandler(DOOPHandler.createDOOPHandler()); } + if (fun.isExport()) { //if(fun.annotations...) AccessHandler accessHandler = createHandlerIfExist(fun,fun.annotations,AccessHandler.class); @@ -648,6 +662,17 @@ public class ContractProcess { jo.add("loadContract", JsonUtil.parseObject(ret)); jo.addProperty("status", ret.status.merge(onCreate.status).toString()); LOGGER.debug("result: " + jo.toString()); + + // doipModule的话,拉起DoipServer服务端口 + if(cn.getYjsType() == YjsType.DoipModule) { + // 只有一台机器去更新Router中的repoInfo就可以了 + if(JavaScriptEntry.shardingID == 0) { + DoipClusterServer server = DoipClusterServer.getDOOPServerInstance(); + server.updateRepoInfo(contract, cn); + } + invokeOnStartingDoipServer(cn, contract.getCreateParam()); + } + return jo.toString(); } catch (Exception e) { ByteArrayOutputStream bo = new ByteArrayOutputStream(); @@ -657,6 +682,76 @@ public class ContractProcess { } } + public void invokeOnStartingDoipServer(ContractNode cn, JsonElement arg) { + ContractRequest onStartingDoipServer = new ContractRequest(); + onStartingDoipServer.setAction("invokeOnStartingDoipServer"); + if (arg == null) { + if (engine != null && engine.getManifest() != null && engine.getManifest().createParam != null) + arg = engine.getManifest().createParam; + else + arg = new JsonPrimitive(""); + } + onStartingDoipServer.setArg(arg); + LOGGER.debug("invoke onStartingDoipServer, param:" + onStartingDoipServer.getArg().toString()); + onStartingDoipServer.setRequester(contract.getOwner()); + if (contract.getDoipFlag() && null != contract.getDOI() && !contract.getDOI().isEmpty()) { + onStartingDoipServer.setRequesterDOI(contract.getDOI()); + } else { + onStartingDoipServer.setRequesterDOI("empty"); + } + FunctionNode funNode = cn.getFunction("invokeOnStartingDoipServer"); + + try { + JsonElement onStartingDoipServerRes = invoke(onStartingDoipServer, funNode).result; + LOGGER.info("Fetch the onStartingDoipServerRes from router successfully, the result is " + onStartingDoipServerRes); + if(onStartingDoipServerRes.isJsonObject()) { + JsonObject onStartingDoipServerJO = onStartingDoipServerRes.getAsJsonObject(); + if (!onStartingDoipServerJO.has("doipAddr")) { + throw new Exception("the doipAddr is improper"); + } else { + JsonElement doipAddrJE = onStartingDoipServerJO.get("doipAddr"); + if(doipAddrJE.isJsonArray()) { + JsonArray doipAddrJA = doipAddrJE.getAsJsonArray(); + for(int i = 0 ; i < doipAddrJA.size() ; i++) { + DoipClusterServer.startDoipServer(doipAddrJA.get(i).getAsString()); + } + } else { + DoipClusterServer.startDoipServer(doipAddrJE.getAsString()); + } + } + } else { + throw new Exception("the onStartingDoipServerRes doesn't return the correct json result"); + } + + } catch (Exception e) { + LOGGER.error("DoipLocalSingleton cannot starts properly, plz check the invokeOnStartingDoipServer function"); + e.printStackTrace(); + } + } + + public void invokeOnStartingDoipServer2(JsonElement arg) { + Object[] funcArgs = new Object[3]; + funcArgs[0] = JSONTool.convertJsonElementToMirror(arg); + funcArgs[1] = contract.getOwner(); + if (contract.getDoipFlag() && null != contract.getDOI() && !contract.getDOI().isEmpty()) { + funcArgs[2] = contract.getDOI(); + } else { + funcArgs[2] = "empty"; + } + Object result = engine.invokeFunction("invokeOnStartingDoipServer", funcArgs); + Map resMap = (Map) result; + try { + if (!resMap.containsKey("doipAddr")) { + throw new Exception("the doipAddr is improper"); + } else { + DoipClusterServer.startDoipServer(resMap.get("doipAddr")); + } + } catch (Exception e) { + LOGGER.error("DoipLocalSingleton cannot starts properly, plz check the invokeOnStartingDoipServer function"); + e.printStackTrace(); + } + } + private void handleLog() { for (FunctionNode fun : cn.getFunctions()) { StringBuilder detail = new StringBuilder(); diff --git a/src/main/java/org/bdware/sc/boundry/utils/ClusterUtil.java b/src/main/java/org/bdware/sc/boundry/utils/ClusterUtil.java index f6b01b4..7154e24 100644 --- a/src/main/java/org/bdware/sc/boundry/utils/ClusterUtil.java +++ b/src/main/java/org/bdware/sc/boundry/utils/ClusterUtil.java @@ -1,23 +1,9 @@ package org.bdware.sc.boundry.utils; -import com.google.gson.JsonArray; import org.bdware.sc.boundry.JavaScriptEntry; import org.bdware.sc.compiler.PermissionStub; -import org.bdware.sc.engine.JSONTool; import org.bdware.sc.node.Permission; -import org.bdware.sc.util.JsonUtil; import wrp.jdk.nashorn.internal.objects.NativeArray; -import wrp.jdk.nashorn.internal.runtime.PropertyMap; -import wrp.jdk.nashorn.internal.runtime.ScriptObject; -import wrp.jdk.nashorn.internal.scripts.JO; - -import javax.crypto.*; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import javax.xml.bind.DatatypeConverter; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; @PermissionStub(permission = Permission.Cluster) public class ClusterUtil { diff --git a/src/main/java/org/bdware/sc/compiler/YJSCompiler.java b/src/main/java/org/bdware/sc/compiler/YJSCompiler.java index 8aea732..45b9f93 100644 --- a/src/main/java/org/bdware/sc/compiler/YJSCompiler.java +++ b/src/main/java/org/bdware/sc/compiler/YJSCompiler.java @@ -5,11 +5,13 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.DiagnosticErrorListener; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bdware.doip.audit.EndpointConfig; import org.bdware.sc.engine.YJSFilter; import org.bdware.sc.node.*; import org.bdware.sc.parser.JavaScriptLexer; import org.bdware.sc.parser.YJSParser; import org.bdware.sc.parser.YJSParser.ProgramContext; +import org.bdware.sc.server.DoipClusterServer; import org.bdware.sc.util.JsonUtil; import org.bdware.sc.visitor.ContractReader; import wrp.jdk.nashorn.internal.objects.Global; @@ -118,6 +120,17 @@ public class YJSCompiler { JsonUtil.GSON.fromJson( new InputStreamReader(manifestInput), ContractManifest.class); + // DOOP relevant logic + ZipEntry routerConfig = zf.getEntry("/router.json"); + if (null == routerConfig) { + routerConfig = zf.getEntry("router.json"); + } + if(null != routerConfig) { + InputStream routerInput = zf.getInputStream(routerConfig); + EndpointConfig endpointConfig = JsonUtil.GSON.fromJson(new InputStreamReader(routerInput), EndpointConfig.class); + DoipClusterServer.createDOOPServerInstance(endpointConfig); + } + // 如果没有就不限制,根据gas进行插装 if (0L != cm.getInsnLimit()) { LOGGER.info("++++++++++++++++++++++true"); diff --git a/src/main/java/org/bdware/sc/compiler/ap/DOOP.java b/src/main/java/org/bdware/sc/compiler/ap/DOOP.java new file mode 100644 index 0000000..630c19a --- /dev/null +++ b/src/main/java/org/bdware/sc/compiler/ap/DOOP.java @@ -0,0 +1,34 @@ +package org.bdware.sc.compiler.ap; + +import org.bdware.sc.bean.DoipOperationInfo; +import org.bdware.sc.compiler.AnnotationProcessor; +import org.bdware.sc.engine.hook.DOOPHandler; +import org.bdware.sc.handler.DOOPRequestHandler; +import org.bdware.sc.node.AnnotationNode; +import org.bdware.sc.node.ContractNode; +import org.bdware.sc.node.FunctionNode; + + +// DOOP is designed for DoipModule which contains specific functions for RepositoryHandler +public class DOOP extends AnnotationProcessor { + @Override + public void processFunction(AnnotationNode anno, ContractNode contractNode, FunctionNode functionNode) { + // 通过DOOP注解,解析对应的值,并放进对应的FunctionNode中 + // 注解必须暴露出来昂!!! + functionNode.setIsExport(true); + functionNode.setIsDoipOperation(true); + functionNode.setDoipOperationInfo(DoipOperationInfo.create(anno, contractNode)); + // functionNode.setFunctionName(functionNode.getDoipOperationInfo().operationName); + + // 维护DOOPRequestHandler + DOOPRequestHandler.createHandler(); + DOOPRequestHandler.instance.addDoipOperation(functionNode); + + // 维护DOOPHandler + DOOPHandler.createDOOPHandler(); + DOOPHandler.instance.putFuncNameAndDoipOperationsMapping(functionNode); + + // 维护ContractNode,functionName is useless, use BasicOperation to map the corresponding functionNode + // contractNode.updateFunctionMap(functionNode.functionName, functionNode.getDoipOperationInfo().operationName); + } +} diff --git a/src/main/java/org/bdware/sc/engine/DesktopEngine.java b/src/main/java/org/bdware/sc/engine/DesktopEngine.java index 846e093..20a9636 100644 --- a/src/main/java/org/bdware/sc/engine/DesktopEngine.java +++ b/src/main/java/org/bdware/sc/engine/DesktopEngine.java @@ -518,15 +518,15 @@ public class DesktopEngine extends JSEngine { input.getRequester().startsWith("event"))) { Object ret; if (fun.isView()) { - ret = executeWithoutLock(fun, input); + ret = executeWithoutLock(fun, input, null); } else { synchronized (this) { - ret = executeWithoutLock(fun, input); + ret = executeWithoutLock(fun, input, null); } } //System.out.println("[DesktopEngine MaskConfig]"+ContractProcess.instance.getProjectConfig().getMaskConfig().config.toString()); - ContractResult contractRes = new ContractResult(Status.Success, (JsonElement) ret); + ContractResult contractRes = new ContractResult(Status.Success, JSONTool.convertMirrorToJson(ret)); if (ppc != null) { contractRes.extraGas = ppc.extraGas; contractRes.executionGas = ppc.cost; @@ -581,9 +581,7 @@ public class DesktopEngine extends JSEngine { } catch (ScriptException e) { Throwable cause = e.getCause(); e.printStackTrace(); - return new ContractResult( - Status.Exception, - new JsonPrimitive(extractException(e.getMessage(), extract(cn, cause)))); + return wrapperException(e, fun); } catch (Throwable e) { ByteArrayOutputStream bo1 = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(bo1); @@ -606,12 +604,16 @@ public class DesktopEngine extends JSEngine { } - private Object executeWithoutLock(FunctionNode fun, ContractRequest input) throws ScriptException, NoSuchMethodException { + public Object executeWithoutLock(FunctionNode fun, ContractRequest input, Object injectedArg) throws ScriptException, NoSuchMethodException { // long start = System.currentTimeMillis(); ArgPacks argPacks = new ArgPacks(input, input.getArg(), null); + if(injectedArg != null) { + argPacks.arg = injectedArg; + } for (AnnotationHook handler : fun.beforeExecutionAnnotations()) { argPacks = handler.handle(this, argPacks); } + // actually invoke! if (argPacks.ret == null) { argPacks.ret = @@ -619,7 +621,7 @@ public class DesktopEngine extends JSEngine { input.getAction(), (fun.isHandler() ? JsonUtil.fromJson(input.getArg(), Event.class) - : JSONTool.convertJsonElementToMirror(input.getArg())), + : JSONTool.convertJsonElementToMirror(argPacks.arg)), input.getRequester(), input.getRequesterDOI()); } diff --git a/src/main/java/org/bdware/sc/engine/JSONTool.java b/src/main/java/org/bdware/sc/engine/JSONTool.java index a759703..abac953 100644 --- a/src/main/java/org/bdware/sc/engine/JSONTool.java +++ b/src/main/java/org/bdware/sc/engine/JSONTool.java @@ -41,6 +41,14 @@ public class JSONTool { return null; } + public static Object convertJsonElementToMirror(Object input) { + if(input instanceof JsonElement) { + return convertJsonElementToMirror((JsonElement) input); + } else { + return input; + } + } + private static JsonElement convertMirrorToJsonInternal(Object obj, Set recorded) { if (recorded.contains(obj)) return JsonNull.INSTANCE; if (obj == null) return JsonNull.INSTANCE; diff --git a/src/main/java/org/bdware/sc/engine/hook/AccessHandler.java b/src/main/java/org/bdware/sc/engine/hook/AccessHandler.java index f8aa908..599cb08 100644 --- a/src/main/java/org/bdware/sc/engine/hook/AccessHandler.java +++ b/src/main/java/org/bdware/sc/engine/hook/AccessHandler.java @@ -3,6 +3,8 @@ package org.bdware.sc.engine.hook; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.bdware.sc.JSEngine; import org.bdware.sc.bean.ContractRequest; import org.bdware.sc.boundry.ScriptReturnException; @@ -12,13 +14,12 @@ import org.bdware.sc.node.AnnotationHook; import org.bdware.sc.node.AnnotationNode; import org.bdware.sc.node.ArgPacks; import org.bdware.sc.node.FunctionNode; -import wrp.jdk.nashorn.internal.objects.Global; -import wrp.jdk.nashorn.internal.runtime.Context; import wrp.jdk.nashorn.internal.runtime.PropertyMap; import wrp.jdk.nashorn.internal.scripts.JO; @YJSAnnotation(name = "Access") public class AccessHandler implements AnnotationHook { + static Logger LOGGER = LogManager.getLogger(AccessHandler.class); public String acFunction; public boolean requireSign; @@ -46,14 +47,19 @@ public class AccessHandler implements AnnotationHook { } return argPacks; } - if (!argPacks.request.verifySignature()){ - argPacks.request.setRequester(null); - } + if (!argPacks.request.verifySignature()) { + LOGGER.info("verify failed! clear requester," + argPacks.request.getContentStr() + + " -> " + argPacks.request.getPublicKey() + "sign:" + argPacks.request.getSignature()); + //TODO + //FIXME here! + // argPacks.request.setRequester(null); + } else + LOGGER.info("verify success!" + argPacks.request.getRequester()); if (acFunction == null) return argPacks; DesktopEngine de = (DesktopEngine) desktopEngine; try { - ContractRequest input = argPacks.request; + ContractRequest input = argPacks.request; JO jo = new JO(PropertyMap.newMap()); jo.put("requester", input.getRequester(), false); jo.put("action", input.getAction(), false); diff --git a/src/main/java/org/bdware/sc/engine/hook/DOOPHandler.java b/src/main/java/org/bdware/sc/engine/hook/DOOPHandler.java new file mode 100644 index 0000000..bbed912 --- /dev/null +++ b/src/main/java/org/bdware/sc/engine/hook/DOOPHandler.java @@ -0,0 +1,228 @@ +package org.bdware.sc.engine.hook; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.bdware.doip.codec.doipMessage.DoipMessage; +import org.bdware.doip.codec.doipMessage.DoipMessageFactory; +import org.bdware.doip.codec.doipMessage.DoipResponseCode; +import org.bdware.doip.codec.operations.BasicOperations; +import org.bdware.sc.JSEngine; +import org.bdware.sc.bean.ContractRequest; +import org.bdware.sc.boundry.ScriptReturnException; +import org.bdware.sc.entity.DoipMessagePacker; +import org.bdware.sc.node.AnnotationHook; +import org.bdware.sc.node.ArgPacks; +import org.bdware.sc.node.FunctionNode; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class DOOPHandler implements AnnotationHook { + public static Map funcNameToDoipOperations; + public static DOOPHandler instance; + public DOOPHandler() { + funcNameToDoipOperations = new HashMap<>(); + } + + public static DOOPHandler createDOOPHandler() { + if(instance == null) { + instance = new DOOPHandler(); + } + + return instance; + } + + public void putFuncNameAndDoipOperationsMapping(FunctionNode fn) { + String basicOperationsString = fn.getDoipOperationInfo().operationType; + BasicOperations operation = BasicOperations.Unknown; + for(BasicOperations basicOperation : BasicOperations.values()) { + if(basicOperation.toString().equals(basicOperationsString)) { + operation = basicOperation; + } + } + funcNameToDoipOperations.put(fn.getFunctionName(), operation); + } + + @Override + public ArgPacks handle(JSEngine desktopEngine, ArgPacks argPacks) { + Object arg = argPacks.arg; + DoipMessagePacker doipMsgPackerArg = new DoipMessagePacker(); + + if (arg instanceof DoipMessagePacker){ + doipMsgPackerArg = (DoipMessagePacker)arg; + } else { + // validate http request's params + ContractRequest httpReq = argPacks.request; + validateHTTPRequestArgs(httpReq); + + // set doipMsgPackerArg struct's params + doipMsgPackerArg.setSource("http"); + doipMsgPackerArg.rawDoipMsg = convertHttpRequestToDoipMessage(httpReq); + } + + argPacks.arg = doipMsgPackerArg; + return argPacks; + } + + public static void validateHTTPRequestArgs(ContractRequest httpReq) { + JsonElement originArgs = httpReq.getArg(); + JsonElement httpArgs = JsonParser.parseString(originArgs.getAsString()); + BasicOperations curOp = funcNameToDoipOperations.get(httpReq.getAction()); + + // get args rules and validate http args + JsonElement httpArgsRules = getRulesForHTTPRequest(curOp); + ArgSchemaVisitor visitor = new ArgSchemaVisitor(httpArgs); + validateJsonElementRulesByArgSchemaVisitor(httpArgsRules, visitor); + } + + public static JsonElement getRulesForHTTPRequest(BasicOperations basicOperation) { + switch(basicOperation) { + case Hello: + case Delete: + case ListOps: + return JsonParser.parseString("{\"!header\":{\"!identifier\":\"string\"}}"); + case Create: + case Update: + return JsonParser.parseString("{\"!header\":{\"!identifier\":\"string\"}, \"!body\":\"string\"}"); + case Search: + return JsonParser.parseString("{\"!header\":{\"!identifier\":\"string\", \"!attributes\":{\"!query\":\"string\", \"!pageNum\":\"int\", \"!pageSize\":\"int\", \"!type\":\"string\"}}}"); + case Retrieve: + return JsonParser.parseString("{\"!header\":{\"!identifier\":\"string\", \"attributes\":{\"element\":\"string\", \"includeElementData\":\"boolean\"}}}"); + case Extension: + case Unknown: + default: + return null; + } + } + + public static JsonElement getRulesForJsonResponse(BasicOperations basicOperations) { + switch (basicOperations) { + case Hello: + case Retrieve: + case Create: + case Update: + case Search: + case ListOps: + return JsonParser.parseString("{\"!header\":{\"!response\":\"string\"}, \"!body\":\"string\"}"); + case Delete: + return JsonParser.parseString("{\"!header\":{\"!response\":\"string\"}, \"body\":\"string\"}"); + case Extension: + case Unknown: + default: + return null; + } + } + + public static DoipMessage convertHttpRequestToDoipMessage(ContractRequest httpReq) { + BasicOperations httpOperation = funcNameToDoipOperations.get(httpReq.getAction()); + JsonObject jsonParams = JsonParser.parseString(httpReq.getArg().getAsString()).getAsJsonObject(); + // taking Extension into consideration + JsonObject header = jsonParams.get("header") != null ? jsonParams.get("header").getAsJsonObject() : null; + JsonObject body = jsonParams.get("body") != null ? jsonParams.get("body").getAsJsonObject() : null; + DoipMessage doipMessage = null; + switch(httpOperation) { + case Hello: + case Delete: + case ListOps: + doipMessage = new DoipMessageFactory.DoipMessageBuilder().createRequest(header.get("identifier").getAsString(), httpOperation.getName()).create(); + break; + case Create: + case Update: + doipMessage = new DoipMessageFactory.DoipMessageBuilder() + .createRequest(header.get("identifier").getAsString(), httpOperation.getName()) + .setBody(body.getAsString().getBytes(StandardCharsets.UTF_8)) + .create(); + break; + case Search: + DoipMessageFactory.DoipMessageBuilder searchBuilder = new DoipMessageFactory.DoipMessageBuilder() + .createRequest(header.get("identifier").getAsString(), httpOperation.getName()); + JsonElement query = header.get("query"); + if(query != null) searchBuilder.addAttributes("query", query.getAsString()); + JsonElement pageNum = header.get("pageNum"); + if(pageNum != null) searchBuilder.addAttributes("pageNum", pageNum.getAsInt()); + JsonElement pageSize = header.get("pageSize"); + if(pageSize != null) searchBuilder.addAttributes("pageSize", pageSize.getAsInt()); + JsonElement type = header.get("type"); + if(type != null) searchBuilder.addAttributes("type", type.getAsString()); + + doipMessage = searchBuilder.create(); + break; + case Retrieve: + doipMessage = new DoipMessageFactory.DoipMessageBuilder().createRequest(header.get("identifier").getAsString(), httpOperation.getName()).create(); + JsonElement element = header.get("element"); + JsonElement includeElementData = header.get("includeElementData"); + if(element != null) doipMessage.header.parameters.addAttribute("element", element.getAsString()); + if(includeElementData != null && includeElementData.getAsBoolean()) doipMessage.header.parameters.addAttribute("includeElementData", "true"); + break; + case Extension: + DoipMessageFactory.DoipMessageBuilder extensionBuilder = new DoipMessageFactory.DoipMessageBuilder(); + if(header != null) { + if(header.get("identifier") != null) { + extensionBuilder = extensionBuilder + .createRequest(header.get("identifier").getAsString(), httpOperation.getName()); + } + + Set> entries = header.entrySet(); + for (Map.Entry entry : entries) { + extensionBuilder.addAttributes(entry.getKey(), entry.getValue()); + } + } + + if(body != null) { + extensionBuilder.setBody(body.getAsString().getBytes(StandardCharsets.UTF_8)); + } + + doipMessage = extensionBuilder.create(); + break; + case Unknown: + default: + break; + } + + return doipMessage; + } + + public static DoipMessage convertJsonResponseToDoipMessage(FunctionNode fn, JsonElement jsonResponse, DoipMessage msg) { + BasicOperations curOp = funcNameToDoipOperations.get(fn.getFunctionName()); + JsonObject jsonParams = jsonResponse.getAsJsonObject(); + + // validate json response + JsonElement jsonResponseRules = getRulesForJsonResponse(curOp); + ArgSchemaVisitor visitor = new ArgSchemaVisitor(jsonResponse); + validateJsonElementRulesByArgSchemaVisitor(jsonResponseRules, visitor); + + JsonObject header = jsonParams.get("header") != null ? jsonParams.get("header").getAsJsonObject() : null; + String body = jsonParams.get("body") != null ? jsonParams.get("body").getAsString() : null; + + if(header != null) { + String headerRespCode = header.get("response") != null ? header.get("response").getAsString() : null; + if(headerRespCode != null) { + for (DoipResponseCode responseCode : DoipResponseCode.values()) { + if(responseCode.toString().equals(headerRespCode)) { + msg.header.parameters.response = responseCode; + break; + } + } + } + } + + if(body != null) { + msg.body.encodedData = body.getBytes(StandardCharsets.UTF_8); + } + + return msg; + } + + public static void validateJsonElementRulesByArgSchemaVisitor(JsonElement jsonElement, ArgSchemaVisitor visitor) { + visitor.visit(jsonElement); + if (!visitor.getStatus()) { + JsonObject jo = new JsonObject(); + jo.addProperty("msg", visitor.getException()); + jo.addProperty("code", visitor.errorCode); + throw new ScriptReturnException(jo); + } + } +} diff --git a/src/main/java/org/bdware/sc/entity/DoipMessagePacker.java b/src/main/java/org/bdware/sc/entity/DoipMessagePacker.java new file mode 100644 index 0000000..691f970 --- /dev/null +++ b/src/main/java/org/bdware/sc/entity/DoipMessagePacker.java @@ -0,0 +1,25 @@ +package org.bdware.sc.entity; + +import org.bdware.doip.codec.doipMessage.DoipMessage; + +public class DoipMessagePacker { + // the DoipMessagePacker is raised by http/doip + public String source; + // the well-composed DoipMessage + public DoipMessage rawDoipMsg; + + public DoipMessagePacker() {} + + public DoipMessagePacker(String source, DoipMessage rawMsg) { + this.source = source; + this.rawDoipMsg = rawMsg; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } +} diff --git a/src/main/java/org/bdware/sc/handler/DOOPRequestHandler.java b/src/main/java/org/bdware/sc/handler/DOOPRequestHandler.java new file mode 100644 index 0000000..c0c1c38 --- /dev/null +++ b/src/main/java/org/bdware/sc/handler/DOOPRequestHandler.java @@ -0,0 +1,162 @@ +package org.bdware.sc.handler; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import io.netty.channel.ChannelHandlerContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.doip.codec.doipMessage.DoipMessage; +import org.bdware.doip.codec.operations.BasicOperations; +import org.bdware.doip.endpoint.server.DoipRequestHandler; +import org.bdware.doip.endpoint.server.NettyServerHandler; +import org.bdware.doip.endpoint.server.RepositoryHandler; +import org.bdware.sc.ContractProcess; +import org.bdware.sc.bean.ContractRequest; +import org.bdware.sc.engine.hook.DOOPHandler; +import org.bdware.sc.entity.DoipMessagePacker; +import org.bdware.sc.node.FunctionNode; + +import java.util.HashMap; +import java.util.Map; + +public class DOOPRequestHandler implements DoipRequestHandler, RepositoryHandler { + public Map doipFunctionNodeMap; + static Logger logger = LogManager.getLogger(NettyServerHandler.class); + static Gson gson; + + public static DOOPRequestHandler instance; + + public DOOPRequestHandler() { + doipFunctionNodeMap = new HashMap<>(); + gson = new Gson(); + } + + public static DOOPRequestHandler createHandler() { + if(instance == null) { + instance = new DOOPRequestHandler(); + } + + return instance; + } + + public void addDoipOperation(FunctionNode function) { + doipFunctionNodeMap.put(function.getDoipOperationInfo().operationName, function); + } + + @Override + public DoipMessage onRequest(ChannelHandlerContext ctx, DoipMessage msg) { + String str = msg.header.parameters.operation; + logger.debug("[Call operation] name: " + str); + if (str != null) { + FunctionNode fn; + fn = doipFunctionNodeMap.get(str); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, msg); + } + } + return null; + } + + @Override + public DoipMessage handleHello(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.Hello.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation Hello is not provided"); + } + return null; + } + + @Override + public DoipMessage handleListOps(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.ListOps.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation ListOps is not provided"); + } + + return null; + } + + @Override + public DoipMessage handleCreate(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.Create.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation Create is not provided"); + } + return null; + } + + @Override + public DoipMessage handleUpdate(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.Update.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation Update is not provided"); + } + + return null; + } + + @Override + public DoipMessage handleDelete(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.Delete.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation Delete is not provided"); + } + return null; + } + + @Override + public DoipMessage handleRetrieve(DoipMessage request) { + FunctionNode fn = doipFunctionNodeMap.get(BasicOperations.Retrieve.getName()); + if (fn == null) fn = doipFunctionNodeMap.get(BasicOperations.Unknown.getName()); + if (fn != null) { + return buildRequestAndInvokeEngine(fn, request); + } else { + logger.error("DoipOperation Retrieve is not provided"); + } + + return null; + } + + public DoipMessage buildRequestAndInvokeEngine(FunctionNode fn, DoipMessage msg) { + ContractRequest contractRequest = constructContractRequest(fn, msg); + DoipMessagePacker arg = new DoipMessagePacker("doip", msg); + try { + // 改变调用的函数 + 构造DoipMessagePacker + Object ret = ContractProcess.instance.engine.executeWithoutLock(fn, contractRequest, arg); + return DOOPHandler.convertJsonResponseToDoipMessage(fn, (JsonElement) ret, msg); + } catch (Exception e) { + e.printStackTrace(); + logger.error("buildRequestAndInvokeEngine has something wrong, executeWithoutLock err or validateJsonElementRulesByArgSchemaVisitor err"); + } + + return null; + } + + public ContractRequest constructContractRequest(FunctionNode fn, DoipMessage request) { + ContractRequest cr = new ContractRequest(); + cr.setContractID(""); + if(request.credential == null) { + cr.setRequester(null); + } else { + cr.setRequester(request.credential.getSigner()); + } + cr.setAction(fn.functionName); + return cr; + } +} diff --git a/src/main/java/org/bdware/sc/server/DoipClusterServer.java b/src/main/java/org/bdware/sc/server/DoipClusterServer.java new file mode 100644 index 0000000..e202388 --- /dev/null +++ b/src/main/java/org/bdware/sc/server/DoipClusterServer.java @@ -0,0 +1,156 @@ +package org.bdware.sc.server; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.doip.audit.EndpointConfig; +import org.bdware.doip.audit.EndpointInfo; +import org.bdware.doip.audit.client.AuditIrpClient; +import org.bdware.doip.endpoint.server.DoipListenerConfig; +import org.bdware.doip.endpoint.server.DoipServerImpl; +import org.bdware.doip.endpoint.server.DoipServiceInfo; +import org.bdware.irp.exception.IrpClientException; +import org.bdware.irp.stateinfo.StateInfoBase; +import org.bdware.sc.ContractProcess; +import org.bdware.sc.bean.Contract; +import org.bdware.sc.bean.RouteInfo; +import org.bdware.sc.handler.DOOPRequestHandler; +import org.bdware.sc.node.ContractNode; +import org.bdware.sc.node.FunctionNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.bdware.doip.audit.EndpointConfig.defaultDOIPServerPort; +import static org.bdware.doip.audit.EndpointConfig.defaultRepoType; + +public class DoipClusterServer extends DoipServerImpl { + static EndpointConfig config; + static AuditIrpClient repoIrpClient; + static DoipClusterServer instance; + // LOGGER + private static final Logger LOGGER = LogManager.getLogger(ContractProcess.class); + + public DoipClusterServer(EndpointConfig config) { + super(resolveInfo(config)); + DoipClusterServer.config = config; + } + + public static void createDOOPServerInstance(EndpointConfig config) { + instance = new DoipClusterServer(config); + } + + public static DoipClusterServer getDOOPServerInstance() { + return instance; + } + + public static void startDoipServer(String arg) throws InterruptedException { + final String doipAddr = arg; + + Thread doipServerThread = new Thread(){ + @Override + public void run() { + try { + DoipLocalSingleton.run(doipAddr); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + + doipServerThread.start(); + } + + private static DoipServiceInfo resolveInfo(EndpointConfig config) { + repoIrpClient = new AuditIrpClient(config); + EndpointInfo info = repoIrpClient.getEndpointInfo(); + if (info == null) { + String content = "{\"date\":\"2022-1-13\",\"name\":\"testrepoforaibd\",\"doId\":\"bdware.test.local/Repo\",\"address\":\"tcp://127.0.0.1:" + defaultDOIPServerPort + "\",\"status\":\"已审核\",\"protocol\":\"DOIP\",\"pubKey\":\"empty\",\"version\":\"2.1\"}"; + info = EndpointInfo.fromJson(content); + } + List infos = new ArrayList<>(); + try { + infos.add(new DoipListenerConfig(info.getURI(), info.getVersion())); + } catch (Exception e) { + e.printStackTrace(); + } + DoipServiceInfo ret = new DoipServiceInfo(info.getDoId(), info.getPubKey(), defaultRepoType, infos); + return ret; + } + + public void updateRepoInfo(Contract contract, ContractNode cn) throws IrpClientException { + String repoIdentifier = "bdtest/" + config.repoName; + StateInfoBase repoInfo = new StateInfoBase(); + + repoInfo.identifier = repoIdentifier; + repoInfo.handleValues = new JsonObject(); + + JsonObject repoHandleValues = new JsonObject(); + + JsonObject createParams = contract.getCreateParam().getAsJsonObject(); + // 放置集群信息 + JsonElement clusterInfo = createParams.get("clusterInfo"); + + // doipOperationName和对应的routeFunctionName的对应关系,存储方式为doipOperationName: routeFunctionName + JsonObject methodRouteInfoMap = new JsonObject(); + // 所有Router中用得到的函数(例如Route函数和Route函数依赖的相关函数) + JsonObject functions = new JsonObject(); + + // 维护RouteInfo,将RouteInfo和doipOperationName的映射关系,以及所有Router中用得到的函数都维护好 + maintainRouteInfo(cn, methodRouteInfoMap, functions); + + if(clusterInfo != null) repoHandleValues.add("clusterInfo", clusterInfo); + if(!functions.equals(new JsonObject())) repoHandleValues.add("functions", functions); + if(!methodRouteInfoMap.equals(new JsonObject())) repoHandleValues.add("routeInfo", methodRouteInfoMap); + repoInfo.handleValues.addProperty("cluster", repoHandleValues.toString()); + + String updateRepoInfoRes = repoIrpClient.reRegister(repoInfo); + if (updateRepoInfoRes.equals("success")) { + LOGGER.info("Update cluster info to router successfully"); + } else if (updateRepoInfoRes.equals("failed")) { + LOGGER.error("Failed to update cluster info to router"); + } else { + LOGGER.warn("Oops...The result of updating clusterInfo to the router is " + updateRepoInfoRes); + } + } + + public void maintainRouteInfo(ContractNode cn, JsonObject methodRouteInfoMap, JsonObject functions) { + // all functions存了ContractNode中,所有的FunctionNode + List allFunctions = cn.getFunctions(); + // gson是Gson工具类昂!!! + Gson gson = new Gson(); + + // 遍历所有的 doipOperationName 和其对应的 doipFunctionNode + for (Map.Entry doipFunctionNodes : DOOPRequestHandler.instance.doipFunctionNodeMap.entrySet()) { + String doipOperationName = doipFunctionNodes.getKey(); + FunctionNode doipFunctionNode = doipFunctionNodes.getValue(); + RouteInfo doipFunctionRouteInfo = doipFunctionNode.getRouteInfo(); + + // 对于routerInfo进行处理,对于routeInfo进行维护 + if(doipFunctionRouteInfo != null) { + methodRouteInfoMap.addProperty(doipOperationName, gson.toJson(doipFunctionRouteInfo)); + if(doipFunctionRouteInfo.funcName != null) { + String routeFunctionName = doipFunctionRouteInfo.funcName; + for(FunctionNode functionNode : allFunctions) { + // find Route Function + if(functionNode.functionName.equals(routeFunctionName)) { + functions.addProperty(functionNode.functionName, functionNode.plainText()); + + // add all dependentFunction to the "functions" struct + for (String dependentFunctionName : functionNode.getDependentFunctions()) { + for (FunctionNode f : cn.getFunctions()) { + if(f.functionName.equals(dependentFunctionName) && !functions.has(dependentFunctionName)) { + functions.addProperty(dependentFunctionName, f.plainText()); + } + } + } + } + } + } + } + } + } +} diff --git a/src/main/java/org/bdware/sc/server/DoipLocalSingleton.java b/src/main/java/org/bdware/sc/server/DoipLocalSingleton.java new file mode 100644 index 0000000..1993032 --- /dev/null +++ b/src/main/java/org/bdware/sc/server/DoipLocalSingleton.java @@ -0,0 +1,71 @@ +package org.bdware.sc.server; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.doip.endpoint.server.DoipListenerConfig; +import org.bdware.doip.endpoint.server.DoipServerImpl; +import org.bdware.doip.endpoint.server.DoipServiceInfo; +import org.bdware.sc.handler.DOOPRequestHandler; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class DoipLocalSingleton { + static Logger LOGGER = LogManager.getLogger(DoipLocalSingleton.class); + + public static void main(String[] arg) throws InterruptedException { + final int port = (arg.length == 0 ? 21042 : Integer.parseInt(arg[0])); + + Thread doipServerThread = new Thread(){ + @Override + public void run() { + try { + DoipLocalSingleton.run(port); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }; + + doipServerThread.start(); + } + + public static void run(int port) throws InterruptedException { + List infos = new ArrayList<>(); + try { + infos.add(new DoipListenerConfig("tcp://127.0.0.1:" + port, "2.1")); + } catch (Exception e) { + e.printStackTrace(); + } + DoipServiceInfo info = new DoipServiceInfo("aibd.govdata.tj/do.3f9c41e6-9f8e-48a0-9220-53f438d40e43", "ownerDEF", "gateRepo", infos); + DoipServerImpl server = new DoipServerImpl(info); + final AtomicInteger count = new AtomicInteger(0); + DOOPRequestHandler handler = DOOPRequestHandler.createHandler(); + server.setRepositoryHandler(handler); + server.start(); + for (; ; ) { + LOGGER.info("Count:" + count.get()); + Thread.sleep(10000); + } + } + + public static void run(String doipAddr) throws InterruptedException { + List infos = new ArrayList<>(); + try { + infos.add(new DoipListenerConfig(doipAddr, "2.1")); + } catch (Exception e) { + e.printStackTrace(); + } + DoipServiceInfo info = new DoipServiceInfo("aibd.govdata.tj/do.3f9c41e6-9f8e-48a0-9220-53f438d40e43", "ownerDEF", "gateRepo", infos); + DoipServerImpl server = new DoipServerImpl(info); + final AtomicInteger count = new AtomicInteger(0); + DOOPRequestHandler handler = DOOPRequestHandler.createHandler(); + server.setRepositoryHandler(handler); + server.start(); + for (; ; ) { + LOGGER.info("Count:" + count.get()); + Thread.sleep(10000); + } + } +}