diff --git a/.gitignore b/.gitignore index 5d947ca..d5d9f4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,12 @@ -# Build and Release Folders -bin-debug/ -bin-release/ -[Oo]bj/ -[Bb]in/ - -# Other files and folders -.settings/ - -# Executables -*.swf -*.air -*.ipa -*.apk - -# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` -# should NOT be excluded as they contain compiler settings and other important -# information for Eclipse / Flash Builder. +/NodeCenterDB/ +/ssl +/rocksdb +/repoConf.json +/ncFile.txt +/handleRecords/* +/handleRecords +/router-frontend/WebContent/client +/router-frontend/WebContent/client/ +/log +/ncconfig.json* +/WebContent \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d4feece --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ + FROM openjdk:8 + +ARG GOPRIVATE=bdware.org/* +ARG GOPROXY=https://goproxy.cn + +LABEL maintainer="caihuaqian@internetapi.cn" +LABEL org.bdware.version="1.5.0" +LABEL org.bdware.version.isproduction="false" +LABEL org.bdware.release-date="2021-08-09" + +COPY ./output /bdcluster +WORKDIR /bdcluster +VOLUME /bdcluster/NodeCenterDB /bdcluster/rocksdb +ENTRYPOINT ["java"] +CMD ["-Dfile.encoding=UTF-8", "-cp", "./libs/*:bdcluster.jar", "org.bdware.server.NodeCenterServer", "-service-port=21040", "-do-repo-ip=localhost","-do-repo-port=21042", "-enable-ssl=./ssl/chained.pem:./ssl/domain.pem"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..4daacbc --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +## Docker镜像制作 +把`Dockerfile`放到一个目录,同时把编译好的`bdcluster.zip`放到同一目录,并解压、制作镜像: +``` +unzip -d ./bdserver/ bdcluster.zip +docker build -t bdware/bdcluster . +``` diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..5e7542f --- /dev/null +++ b/build.gradle @@ -0,0 +1,121 @@ +plugins { + id 'java' + id 'application' +} + +mainClassName = 'org.bdware.server.NodeCenterServer' + +application { + mainClass = mainClassName + applicationDefaultJvmArgs = ['-Dfile.encoding=UTF-8'] +} + +dependencies { + implementation project(":front-base") + // https://mvnrepository.com/artifact/com.jianggujin/IKAnalyzer-lucene + implementation 'com.jianggujin:IKAnalyzer-lucene:8.0.0' + // https://mvnrepository.com/artifact/org.apache.lucene/lucene-core + implementation 'org.apache.lucene:lucene-core:8.9.0' + // https://mvnrepository.com/artifact/org.apache.lucene/lucene-queryparser + implementation 'org.apache.lucene:lucene-queryparser:8.9.0' + // https://mvnrepository.com/artifact/org.apache.lucene/lucene-analyzers-common + implementation 'org.apache.lucene:lucene-analyzers-common:8.9.0' + // https://mvnrepository.com/artifact/org.apache.lucene/lucene-backward-codecs + implementation 'org.apache.lucene:lucene-backward-codecs:8.9.0' + implementation 'org.apache.lucene:lucene-analyzers-smartcn:8.9.0' + + testImplementation 'junit:junit:4.13.2' +} + +jar { + String libs = '' + configurations.runtimeClasspath.each { + libs = libs + " libs/" + it.name + } + + manifest { + attributes 'Manifest-Version': project.version + attributes 'Main-Class': mainClassName + attributes 'Class-Path': libs + } +} + +tasks.processResources.dependsOn(":web-client:copyToCluster") +tasks.processResources.setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + +task copyScript(type: Copy) { + from("../script/") { + include 'ncstart.sh' + include 'ncstop.sh' + include 'ncconfig.json.template' + include 'updateCluster.sh' + } + into "./build/output" + println("copyScript done !") +} + +task copySsl(type: Copy) { + from './ssl' + into './build/output/ssl' +} + +task copyLibs(type: Copy, dependsOn: ["copyScript", 'copySsl']) { + from configurations.runtimeClasspath + into "./build/output/libs/" +} + +//task copyJar(type: Exec, dependsOn: [":front-cluster:jar", ":front-cluster:copyLibs"]) { +// println("copyJar start") +// commandLine "cp", "./build/libs/$project.name-$version" + ".jar", "./build/output/bdcluster.jar" +//} + +task copyJar(type: Copy, dependsOn: [":front-cluster:jar", ":front-cluster:copyLibs"]) { + from "./build/libs/$project.name-${project.version}.jar" + into "./build/output" + rename { String fileName -> "bdcluster.jar" } + doFirst { + println "copyJar start" + } +} + +task copyWebContent(type: Copy) { + from("./WebContent") { + exclude 'client/BaaSClient.html' + exclude 'client/README.md' + exclude 'client/.idea/' + exclude 'client/.gitignore' + exclude '.idea/' + } + into "./build/output/WebContent" +} + +task buildBDServerClusterZip(type: Zip, dependsOn: [":front-cluster:copyWebContent", ":front-cluster:copyScript", + ":front-cluster:copyJar"]) { + from './build/output/' + duplicatesStrategy = DuplicatesStrategy.INCLUDE + archiveFileName = 'bdserver-cluster.zip' + destinationDirectory = file('build/') +} + +task buildBDClusterZip(type: Zip, dependsOn: [":front-cluster:buildBDServerClusterZip"]) { + from('./build/output/') { + include("libs/*") + include("bdcluster.jar") + } + duplicatesStrategy = DuplicatesStrategy.INCLUDE + archiveFileName = 'bdcluster.zip' + destinationDirectory = file('build/') +} + +task buildWebContentZip(type: Zip, dependsOn: [":front-cluster:copyWebContent"]) { + from "./build/output/WebContent" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + archiveFileName = 'ClusterWebContent.zip' + destinationDirectory = file('build/') +} + +task buildBundle(dependsOn: [":front-cluster:buildBDClusterZip", ":front-cluster:buildWebContentZip"]) { + doLast { + println "buildBundle in ./build/output/ successfully" + } +} \ No newline at end of file diff --git a/src/main/java/org/bdware/sc/udp/UDPMessage.java b/src/main/java/org/bdware/sc/udp/UDPMessage.java new file mode 100644 index 0000000..99ea432 --- /dev/null +++ b/src/main/java/org/bdware/sc/udp/UDPMessage.java @@ -0,0 +1,86 @@ +package org.bdware.sc.udp; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + + +public class UDPMessage implements Serializable { + private static final long serialVersionUID = -8103830946473687985L; + int isPart = 0; + public UDPMessageType type; + public int id; // 要标记sender + int order; + int requestID; + byte[] content; + + public UDPMessage() { + } + + public void setIsPart(boolean isPart) { + this.isPart = isPart ? 1 : 0; + } + + public void setContent(byte[] content) { + this.content = content; + } + + public byte[] getContent() { + return content; + } + + static int Len = 6000;// 1000 + + public List split() { + List splited = new ArrayList<>(); +// System.out.println("[UDPMessage split] content.length" + content.length); + if (content.length < Len) { + splited.add(this); + return splited; + } + for (int i = 0; i < content.length; i += Len) { + UDPMessage msg = new UDPMessage(); + msg.isPart = 1; + msg.id = this.id; + msg.order = i / Len; + if (i + Len > content.length) { + msg.content = new byte[content.length - i]; + System.arraycopy(content, i, msg.content, 0, content.length - i); + } else { + msg.content = new byte[Len]; + System.arraycopy(content, i, msg.content, 0, Len); + } + splited.add(msg); + } + return splited; + } + + public byte[] toByteArray() { + try { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + ObjectOutputStream output; + output = new ObjectOutputStream(bo); + output.writeObject(this); + return bo.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static UDPMessage parse(byte[] data, int len) { + try { + ByteArrayInputStream bi = new ByteArrayInputStream(data); + ObjectInputStream input = new ObjectInputStream(bi); + return (UDPMessage) input.readObject(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/main/java/org/bdware/sc/udp/UDPMessageType.java b/src/main/java/org/bdware/sc/udp/UDPMessageType.java new file mode 100644 index 0000000..bf130c4 --- /dev/null +++ b/src/main/java/org/bdware/sc/udp/UDPMessageType.java @@ -0,0 +1,7 @@ +package org.bdware.sc.udp; + +import java.io.Serializable; + +public enum UDPMessageType implements Serializable { + shakeHand, request, reply +} diff --git a/src/main/java/org/bdware/sc/udp/UDPNode.java b/src/main/java/org/bdware/sc/udp/UDPNode.java new file mode 100644 index 0000000..cc119c5 --- /dev/null +++ b/src/main/java/org/bdware/sc/udp/UDPNode.java @@ -0,0 +1,20 @@ +package org.bdware.sc.udp; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import org.bdware.sc.conn.Node; + +public class UDPNode extends Node { + public UDPNode(SocketAddress addr) { + this.addr = addr; + } + + public int id; + public long lastUpdatedTime; + public SocketAddress addr; + + public String getAddr() { + InetSocketAddress inet4 = (InetSocketAddress) addr; + return inet4.getHostString() + ":" + inet4.getPort(); + } +} diff --git a/src/main/java/org/bdware/server/NodeCenterServer.java b/src/main/java/org/bdware/server/NodeCenterServer.java new file mode 100644 index 0000000..42a2601 --- /dev/null +++ b/src/main/java/org/bdware/server/NodeCenterServer.java @@ -0,0 +1,207 @@ +package org.bdware.server; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.ssl.OptionalSslHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.stream.ChunkedWriteHandler; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.config.Configurator; +import org.bdware.sc.DoConfig; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.db.MultiIndexTimeRocksDBUtil; +import org.bdware.server.irp.LocalLHSProxy; +import org.bdware.server.nodecenter.*; +import org.bdware.server.ws.DelimiterCodec; + +import java.io.File; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +public class NodeCenterServer { + public static final String PATH = "/NodeCenterWS"; + private static final String CONFIG_PATH = "ncconfig.json"; + private static final Logger LOGGER = LogManager.getLogger(NodeCenterServer.class); + public static MultiIndexTimeRocksDBUtil nodeHttpLogDB = + new MultiIndexTimeRocksDBUtil("./NodeCenterDB", NCTables.NodeHttpLog.toString()); + public static MultiIndexTimeRocksDBUtil nodeTcpLogDB = + new MultiIndexTimeRocksDBUtil("./NodeCenterDB", NCTables.NodeTcpLog.toString()); + public static ExecutorService threadPool; + public static ScheduledExecutorService scheduledThreadPool = + Executors.newScheduledThreadPool(8); + static SslContext sslContext = null; +// static byte[] delimiter = "wonbifoodie".getBytes(); + + static { + KeyValueDBUtil.setupNC(); + //TimeDBUtil.setupNC(); + Configurator.setLevel( + "io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder", + Level.OFF); + Configurator.setLevel( + "io.netty.handler.codec.http.websocketx.WebSocket08FrameEncoder", + Level.OFF); + } + + public static void parseConf(CMDConf cmdConf) { + LocalLHSProxy.port = cmdConf.doipPort; + LocalLHSProxy.enabled = true; + if (cmdConf.disableDoRepo) { + DoConfig.callContractUsingDOI = false; + } + if (cmdConf.disableLocalLhs) { + LocalLHSProxy.enabled = false; + } + if (!cmdConf.enableSsl.isEmpty()) { + try { + String[] filePaths = cmdConf.enableSsl.split(":"); + File chainedFile = new File(filePaths[0]), keyFile = new File(filePaths[1]); + if (chainedFile.exists() && keyFile.exists()) { + sslContext = SslContextBuilder.forServer(chainedFile, keyFile).build(); + } + LOGGER.debug("Enable ssl, path: " + chainedFile.getPath()); + } catch (Exception e) { + LOGGER.warn("Enabling SSL failed."); + e.printStackTrace(); + } + } + if (cmdConf.debug != null) { + if (!cmdConf.debug.isEmpty()) { + + try { + String[] classes = cmdConf.debug.split(","); + for (String clz : classes) { + Configurator.setLevel(clz, Level.DEBUG); + LOGGER.warn("set debug: " + clz); + } + } catch (Exception e) { + LOGGER.warn(e.getMessage()); + } + } + } + if (cmdConf.overwrite) { + cmdConf.write(CONFIG_PATH); + } + } + + public static void main(String[] args) throws Exception { + File confFile = new File(CONFIG_PATH); + File confTemplate = new File(CONFIG_PATH + ".template"); + if (!confTemplate.exists()) { + CMDConf conf = new CMDConf(); + conf.write(confTemplate.getAbsolutePath()); + } + if (!confFile.exists() && confTemplate.exists()) { + FileUtils.copyFile(confTemplate, confFile); + } + CMDConf cmf = CMDConf.parseConf(CONFIG_PATH).parseArgs(args); + + parseConf(cmf); + + if (LocalLHSProxy.enabled) { + threadPool = Executors.newSingleThreadExecutor(); + threadPool.execute(() -> { + try { + LocalLHSProxy.start(); + } catch (Exception e) { + LOGGER.error("local LHS proxy failed: " + e.getMessage()); + } + }); + } + + listenSocket(cmf.servicePort + 1); + OtherNCProxy.instance.init(); + + startHttp(cmf.servicePort); + //System.out.println("PORT"+cmf.servicePort); + } + + public static void listenSocket(int port) throws Exception { + LOGGER.info("listen socket at " + port); + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + ServerBootstrap b = new ServerBootstrap(); + b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 100) + .localAddress(port) + .childHandler( + new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel arg0) { + arg0.pipeline() + .addLast(new DelimiterCodec()) + .addLast(new NodeCenterFrameHandler()); + } + }); + b.bind(port).sync().channel(); + } + + public static void startHttp(int port) { + LOGGER.info("start at: " + port); + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + ControlledChannelInitializer initializer = new ControlledChannelInitializer(); + b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .localAddress(port) + .childHandler(initializer); + final Channel ch = b.bind(port).sync().channel(); + ch.closeFuture().sync(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + public static void start(int port) { + try { + listenSocket(port + 1); + NCUDPRunner.mainPort = port + 2; + startHttp(port); + } catch (Exception e) { + e.printStackTrace(); + } + } + + static class ControlledChannelInitializer extends ChannelInitializer { + NCHttpHandler handler = new NCHttpHandler(); + + @Override + protected void initChannel(SocketChannel arg0) { + if (sslContext != null) { + arg0.pipeline().addLast(new OptionalSslHandler(sslContext)); + } else { + LOGGER.warn("disable ssl"); + } + arg0.pipeline() + .addLast(new HttpServerCodec()) + .addLast(new HttpObjectAggregator(65536)) + .addLast(new WebSocketServerProtocolHandler(PATH, null, true)) + .addLast(new ChunkedWriteHandler()) + .addLast(handler) + .addLast(new NodeCenterWSFrameHandler()); + } + } +} diff --git a/src/main/java/org/bdware/server/action/DistributeCallback.java b/src/main/java/org/bdware/server/action/DistributeCallback.java new file mode 100644 index 0000000..54a10f4 --- /dev/null +++ b/src/main/java/org/bdware/server/action/DistributeCallback.java @@ -0,0 +1,148 @@ +package org.bdware.server.action; + +import com.google.gson.reflect.TypeToken; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.nodecenter.CMNode; +import org.bdware.server.nodecenter.NodeCenterActions; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +public class DistributeCallback extends ResultCallback { + private static final Logger LOGGER = LogManager.getLogger(DistributeCallback.class); + String sponsorPubkey;// 发起节点的 + Map nodes; + String pubKey; //发起节点的用户公钥 + String signature; // 发起节点的 + int count; // 集群中几个节点已成功启动 + ResultCallback res; // 返回给Node + String fileName; // ypk的文件名 + int index; + String distributeID; //发起节点的分发id + + public DistributeCallback(String ss, String sponID, Map n, ResultCallback re, String p, String s) { + distributeID = ss; + count = 0; + index = 1; + this.res = re; + this.pubKey = p; + this.signature = p; + this.sponsorPubkey = sponID; + this.nodes = n; + } + + // 向集群中没有合约项目的节点发送合约 + public void distributeContractProject(String filePath, String isPrivate) { + LOGGER.debug("[DistributeCallback] distributeContractProject : position----5" + filePath); + + File f = new File(filePath); + fileName = f.getName(); + LOGGER.debug("[DistributeCallback] distributeContractProject : fileName=" + fileName); + LOGGER.debug("[DistributeCallback] nodeNames: " + JsonUtil.toJson(nodes)); + + for (String ID : nodes.keySet()) { + if (ID.equals(sponsorPubkey)) + continue; + CMNode node = NodeCenterActions.nodeInfos.get(ID); + node.connection.sendProject(filePath, isPrivate, pubKey, this); + } + } + + public void NCreceive(String progress) { + Map ret2 = new HashMap<>(); + ret2.put("action", "onDistributeContract"); + ret2.put("progress", "NC is receiving ypk from sponsor,progress is " + progress + "%."); + + Map map = new HashMap<>(); + map.put("action", "onDistribute"); + map.put("content", JsonUtil.toJson(ret2)); + map.put("distributeID", distributeID); + res.onResult(JsonUtil.toJson(map)); + } + + public void onDistribute(String progress) { + Map ret2 = new HashMap<>(); + ret2.put("action", "onDistributeContract"); + ret2.put( + "progress", + "NC is sending ypk to NO." + + index + + " node in the units,progress is " + + progress + + "%."); + if (progress.equals("100.00")) index++; + + Map map = new HashMap<>(); + map.put("action", "onDistribute"); + map.put("distributeID", distributeID); + map.put("content", JsonUtil.toJson(ret2)); + res.onResult(JsonUtil.toJson(map)); + } + + public void onReceive(Map map) { + LOGGER.debug("[DistributeCallback] onReceive : position----9"); + String progress = map.get("progress"); + + if (progress.equals("100")) { + Map args = new HashMap<>(); + + args.put("fileName", fileName); + args.put("pubKey", pubKey); + args.put("signature", signature); + + count++; + + LOGGER.debug(count + "个节点已收完成" + " 总共有" + nodes.size() + " 个节点"); + + if (count == nodes.size()) { + // res返回给前端,合约分发完成 + Map ret2 = new HashMap<>(); + ret2.put("action", "onDistributeFinish"); + ret2.put("progress", "100%"); + Map map_send = new HashMap<>(); + map_send.put("action", "onDistribute"); + map_send.put("over", "true"); + map_send.put("distributeID", distributeID); + map_send.put("content", JsonUtil.toJson(ret2)); + res.onResult(JsonUtil.toJson(map_send)); + //NC delete file + File file = new File("./temp/stateFiles/" + fileName); + if (file.exists() && file.isFile()) + file.delete(); + } + } + } + + @Override + public void onResult(String str) { + LOGGER.debug("[DistributeCallback] str=" + str); + + Map map = JsonUtil.fromJson(str, new TypeToken>() { + }.getType()); + + NodeCenterActions.sync.sleepWithTimeout(map.get("requestID"), this, 60); + + String operation = map.get("operation"); + + switch (operation) { + case "NCreceive": + NCreceive(map.get("progress")); + break; + case "distribute": + distributeContractProject(map.get("receiveFileName"), map.get("isPrivate")); + break; + case "onDistribute": + onDistribute(map.get("progress")); + break; + case "onReceive": + onReceive(map); + break; + default: + LOGGER.debug("[DistributeCallback] undefined operation " + operation); + } + } +} diff --git a/src/main/java/org/bdware/server/action/RequestAllExecutor.java b/src/main/java/org/bdware/server/action/RequestAllExecutor.java new file mode 100644 index 0000000..1ac3550 --- /dev/null +++ b/src/main/java/org/bdware/server/action/RequestAllExecutor.java @@ -0,0 +1,172 @@ +package org.bdware.server.action; + +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.ContractResult; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.nodecenter.CMNode; +import org.bdware.server.nodecenter.ContractExecutor; +import org.bdware.server.nodecenter.MultiPointContractInfo; +import org.bdware.server.nodecenter.NodeCenterActions; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class RequestAllExecutor implements ContractExecutor { + private static final Logger LOGGER = LogManager.getLogger(RequestAllExecutor.class); + MultiPointContractInfo info; + int resultCount; + + public RequestAllExecutor(MultiPointContractInfo minfo, int count) { + info = minfo; + resultCount = count; + } + + public ResultCallback createResultCallback( + final String requestID, final ResultCallback originalCb, int count, String contractID) { + return new Collector(requestID, originalCb, count, contractID); + } + + //only for test ADSP +// public ResultCallback createResultCallback( +// final String requestID, final ResultCallback originalCb, int count,String contractID,String seq) { +// ResultCallback collector = new Collector(requestID, originalCb, count,contractID,seq); +// return collector; +// } + + public void sendRequest(String req, ResultCallback collector) { + List nodes = info.members; + for (String node : nodes) { + CMNode cmNode = NodeCenterActions.nodeInfos.get(node); + if (cmNode == null) { + collector.onResult( + "{\"status\":\"Error\",\"result\":\"node " + + node + + " offline\",\"action\":\"onExecuteContractTrustfully\"}"); + } else { + cmNode.connection.controller.sendMsg(req); + } + } + } + + @Override + public void execute(String requestID, ResultCallback rc, String req) { + JsonObject jo2 = JsonParser.parseString(req).getAsJsonObject(); + String id = jo2.get("contractID").getAsString(); + + //only for test ADSP +// ResultCallback collector = createResultCallback(requestID, rc, resultCount,id,jo2.get("seq").getAsString()); + + ResultCallback collector = createResultCallback(requestID, rc, resultCount, id); + + //TODO NC.sync?有问题? + NodeCenterActions.sync.sleep(requestID, collector); + sendRequest(req, collector); + } + + static class Collector extends ResultCallback { + String contractID; + int total; + String requestID; + AtomicInteger count = new AtomicInteger(0); + + + //only for testADSP +// boolean flag = true; //true表示还没在文件中记录这次调用结果 +// String seq; +// Map res = new ConcurrentHashMap(); + + + //only for test ADSP +// public Collector(String reqID, ResultCallback callback, int c,String id,String seq2) { +// total = c; +// requestID = reqID; +// commiter = callback; +// contractID = id; +// seq = seq2; +// } + ResultCallback committer; + + public Collector(String reqID, ResultCallback callback, int c, String id) { + total = c; + requestID = reqID; + committer = callback; + contractID = id; + } + + @Override + public void onResult(String str) { + JsonObject obj = JsonParser.parseString(str).getAsJsonObject(); + obj.remove("action"); + obj.addProperty("action", "onExecuteContractTrustfully"); + obj.addProperty("seqNum", count.getAndIncrement()); + obj.addProperty("total", total); + + //NodeCenterActions.recoverMap.get(nodePubKey).get(contractID).lastExeSeq++; + + if (committer != null) + committer.onResult(obj.toString()); + if (count.get() < total) { + NodeCenterActions.sync.sleepWithTimeout(requestID, this, 10); + } + + String name = "null"; + if (obj.has("nodeID")) { + String nodePubKey = obj.get("nodeID").getAsString(); + CMNode node = NodeCenterActions.nodeInfos.get(nodePubKey); + name = node.nodeName; + } + LOGGER.debug("收到第 " + count.get() + " 个节点 " + name + " 回复 : " + obj); + + if (obj.has("data")) { + String data = obj.get("data").getAsString(); + ContractResult cr = JsonUtil.fromJson(data, ContractResult.class); + + //only for testADSP +// if(TestADSP.getFlag()){ +// String r = cr.result; +// if(!res.containsKey(r)){ +// res.put(r,0); +// } +// int former = res.get(r); +// res.put(r,++former); +// for(String s : res.keySet()){ +// if(res.get(s) > total / 2 && flag){ +// //写入文件 +// File file = new File(TestADSP.resultPath); +// synchronized (file){ +// try { +// FileWriter fw = new FileWriter(file, true); +// PrintWriter pw = new PrintWriter(fw); +// pw.println(seq + " " + r); +// pw.flush(); +// fw.flush(); +// pw.close(); +// fw.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// flag = false; +// } +// } +// } + + +/* if (cr.status == ContractResult.Status.Error) { //TODO 规范Status的使用 改成 ==Statuc.Error才恢复 + String nodePubKey = obj.get("nodeID").getAsString(); + if(NodeCenterActions.recoverMap.containsKey(nodePubKey)){ + ContractRecord record = NodeCenterActions.recoverMap.get(nodePubKey).get(contractID); + if(record.recoverFlag == RecoverFlag.Fine){ + record.recoverFlag = RecoverFlag.ToRecover; + MasterActions.restartContracts(nodePubKey); + } + } + }*/ + } + } + } +} diff --git a/src/main/java/org/bdware/server/action/RequestOnceExecutor.java b/src/main/java/org/bdware/server/action/RequestOnceExecutor.java new file mode 100644 index 0000000..d9b8a30 --- /dev/null +++ b/src/main/java/org/bdware/server/action/RequestOnceExecutor.java @@ -0,0 +1,56 @@ +package org.bdware.server.action; + +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.conn.ResultCallback; +import org.bdware.server.nodecenter.CMNode; +import org.bdware.server.nodecenter.ContractExecutor; +import org.bdware.server.nodecenter.MultiPointContractInfo; +import org.bdware.server.nodecenter.NodeCenterActions; + +import java.util.concurrent.atomic.AtomicInteger; + +public class RequestOnceExecutor implements ContractExecutor { + private static final Logger LOGGER = LogManager.getLogger(RequestOnceExecutor.class); + MultiPointContractInfo info; + AtomicInteger order = new AtomicInteger(0); + + public RequestOnceExecutor(MultiPointContractInfo minfo) { + info = minfo; + } + + @Override + public void execute(String requestID, ResultCallback rc, String req) { + JsonObject jo2 = (new JsonParser()).parse(req).getAsJsonObject(); + String contractID = jo2.get("contractID").getAsString(); + + ResultCallback cb = + new ResultCallback() { + @Override + public void onResult(String str) { + LOGGER.debug(str); + JsonObject jo = (new JsonParser()).parse(str).getAsJsonObject(); + jo.remove("action"); + jo.addProperty("action", "onExecuteResult"); + LOGGER.debug(jo.toString()); + rc.onResult(jo.toString()); + } + }; + NodeCenterActions.sync.sleep(requestID, cb); + for (int i = 0; i < info.members.size(); i++) { + int size = info.members.size(); + String nodeID = info.members.get(order.incrementAndGet() % size); + CMNode cmNode = NodeCenterActions.nodeInfos.get(nodeID); + + if (cmNode != null) { + cmNode.connection.controller.sendMsg(req); + return; + } + } + rc.onResult( + "{\"status\":\"Error\",\"result\":\"all nodes " + + " offline\",\"action\":\"onExecuteContract\"}"); + } +} diff --git a/src/main/java/org/bdware/server/action/ResponseOnceExecutor.java b/src/main/java/org/bdware/server/action/ResponseOnceExecutor.java new file mode 100644 index 0000000..c195414 --- /dev/null +++ b/src/main/java/org/bdware/server/action/ResponseOnceExecutor.java @@ -0,0 +1,82 @@ +package org.bdware.server.action; + +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.ContractResult; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.nodecenter.CMNode; +import org.bdware.server.nodecenter.ContractExecutor; +import org.bdware.server.nodecenter.MultiPointContractInfo; +import org.bdware.server.nodecenter.NodeCenterActions; + +import java.util.concurrent.atomic.AtomicInteger; + +public class ResponseOnceExecutor implements ContractExecutor { + private static final Logger LOGGER = LogManager.getLogger(ResponseOnceExecutor.class); + MultiPointContractInfo info; + AtomicInteger order = new AtomicInteger(0); + + public ResponseOnceExecutor(MultiPointContractInfo minfo) { + info = minfo; + } + + @Override + public void execute(String requestID, ResultCallback rc, String req) { + executeInternal(requestID, rc, req, 2); + } + + private void executeInternal(String requestID, ResultCallback rc, String req, int count) { + JsonObject jo2 = (new JsonParser()).parse(req).getAsJsonObject(); + String contractID = jo2.get("contractID").getAsString(); + // TODO 标注失效节点,是否选择重新迁移?? + ResultCallback cb = + new ResultCallback() { + @Override + public void onResult(String str) { + LOGGER.debug(str); + JsonObject jo = (new JsonParser()).parse(str).getAsJsonObject(); + jo.remove("action"); + jo.addProperty("action", "onExecuteResult"); + LOGGER.debug(jo.toString()); + if (jo.has("data")) { + String data = jo.get("data").getAsString(); + ContractResult cr = JsonUtil.fromJson(data, ContractResult.class); + if (cr.status != ContractResult.Status.Success && count > 0) { + executeInternal(requestID, rc, req, count - 1); + } else + rc.onResult(jo.toString()); + } else { + JsonObject jo2 = new JsonObject(); + jo2.addProperty("action", "onExecuteResult"); + jo.remove("action"); + jo2.addProperty("data", jo.toString()); + rc.onResult(jo2.toString()); + } + } + }; + NodeCenterActions.sync.sleepWithTimeout(requestID, cb, 5); + if (!sendOnce(req)) + rc.onResult( + "{\"status\":\"Error\",\"data\":\"{\\\"status\\\":\\\"Error\\\",\\\"result\\\":\\\"all nodes offline\\\"}\",\"action\":\"onExecuteContract\"}"); + } + + private boolean sendOnce(String req) { + JsonObject jo2 = (new JsonParser()).parse(req).getAsJsonObject(); + String contractID = jo2.get("contractID").getAsString(); + + for (int i = 0; i < info.members.size(); i++) { + int size = info.members.size(); + String nodeID = info.members.get(order.incrementAndGet() % size); + CMNode cmNode = NodeCenterActions.nodeInfos.get(nodeID); + + if (cmNode != null) { + cmNode.connection.controller.sendMsg(req); + return true; + } + } + return false; + } +} diff --git a/src/main/java/org/bdware/server/irp/LocalLHSProxy.java b/src/main/java/org/bdware/server/irp/LocalLHSProxy.java new file mode 100644 index 0000000..5de7bdd --- /dev/null +++ b/src/main/java/org/bdware/server/irp/LocalLHSProxy.java @@ -0,0 +1,123 @@ +package org.bdware.server.irp; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.util.JsonUtil; +import org.rocksdb.Options; +import org.rocksdb.RocksDB; +import org.rocksdb.RocksDBException; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class LocalLHSProxy { + private static final Logger LOGGER = LogManager.getLogger(LocalLHSProxy.class); + public static String PREFIX = "86.5000.470/"; + public static int port = 18007; + public static boolean enabled = false; + public static RocksDB db; + static RegisterHandler registerHandler; + + static { + RocksDB.loadLibrary(); + } + + public static void main(String[] args) throws Exception { + start(); + } + + public static void start() throws IOException, RocksDBException { + String dbPath = "./handleRecords/"; + Options rockopts = new Options().setCreateIfMissing(true); + rockopts.useFixedLengthPrefixExtractor(15); + LOGGER.info("actual rocksdb path: " + new File(dbPath).getAbsolutePath()); + File lock = new File(dbPath, "LOCK"); + if (lock.exists()) { + LOGGER.trace("remove file " + lock.getAbsolutePath() + ": " + lock.delete()); + } + db = RocksDB.open(rockopts, dbPath); + + registerHandler = new RegisterHandler(db); + + LOGGER.info("listen to " + port); + HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); + + server.createContext("/test", new TestHandler()); + server.createContext("/resolve", new ResolveHandler()); + server.createContext("/local/", registerHandler); + server.createContext("/view", new ViewHandler(db)); + server.start(); + } + + private static Map formData2Dic(String formData) { + Map result = new HashMap<>(); + if (formData == null || formData.trim().length() == 0) { + return result; + } + final String[] items = formData.split("&"); + Arrays.stream(items) + .forEach( + item -> { + final String[] keyAndVal = item.split("="); + if (keyAndVal.length == 2) { + try { + final String key = URLDecoder.decode(keyAndVal[0], "utf8"); + final String val = URLDecoder.decode(keyAndVal[1], "utf8"); + result.put(key, val); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + }); + return result; + } + + static class TestHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) { + String response = "hello world"; + try { + exchange.sendResponseHeaders(200, 0); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + static class ResolveHandler implements HttpHandler { + @Override + public void handle(HttpExchange exchange) { + try { + // 获得查询字符串(get) + String queryString = exchange.getRequestURI().getQuery(); + Map queryStringInfo = formData2Dic(queryString); + String id = queryStringInfo.get("identifier"); + Map respMap; + respMap = registerHandler.handleResolve(queryStringInfo); + respMap.forEach((k, v) -> { + LOGGER.debug(k + "->" + v); + }); + exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*"); + exchange.sendResponseHeaders(200, 0); + OutputStream os = exchange.getResponseBody(); + os.write(JsonUtil.toJson(respMap).getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/org/bdware/server/irp/RegisterHandler.java b/src/main/java/org/bdware/server/irp/RegisterHandler.java new file mode 100644 index 0000000..8026e95 --- /dev/null +++ b/src/main/java/org/bdware/server/irp/RegisterHandler.java @@ -0,0 +1,246 @@ +package org.bdware.server.irp; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import org.apache.commons.io.IOUtils; +import org.bdware.sc.util.JsonUtil; +import org.rocksdb.RocksDB; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +public class RegisterHandler implements HttpHandler { + RocksDB db; + String dbPath = "./handleRecords/"; + + public RegisterHandler(RocksDB rocksDB) { + db = rocksDB; + } + + public Map HandleRequest(Map postInfo) { + Map respMap = new HashMap<>(); + String action = postInfo.get("action"); + switch (action) { + case "resolve": + respMap = handleResolve(postInfo); + break; + case "register": + case "forceRegister": + respMap = handleRegister(postInfo); + break; + case "unregister": + case "forceDelete": + respMap = handleUnRegister(postInfo); + break; + case "reregister": + respMap = handleReregister(postInfo); + break; + default: + System.out.println("error response"); + } + return respMap; + } + + public Map handleRegister(Map reqMap) { + Map respMap = new HashMap<>(); + String type; + switch (reqMap.get("hrType")) { + case "do": + type = "do."; + ViewHandler.handleRecordCount.doCount++; + break; + case "dou": + type = "dou."; + ViewHandler.handleRecordCount.userCount++; + break; + case "doip": + type = "doip."; + ViewHandler.handleRecordCount.repoCount++; + break; + default: + type = "ukw."; + break; + } + String identifier = LocalLHSProxy.PREFIX + type + geneRandomID() + "_bdw"; + reqMap.remove("action"); + reqMap.remove("hrType"); + reqMap.put("identifier", identifier); + String handleRecord = JsonUtil.toJson(reqMap); + try { + db.put(identifier.getBytes(), handleRecord.getBytes()); + System.out.println("successful update do, identifier: " + identifier); + respMap.put("identifier", identifier); + respMap.put("status", "1"); + respMap.put("response", "register DO success, identifier: " + identifier); + ViewHandler.addRegister(); + } catch (Exception e) { + e.printStackTrace(); + respMap.put("status", "0"); + respMap.put("response", "respond failed from LHS"); + } + return respMap; + } + + public Map handleReregister(Map reqMap) { + Map respMap = new HashMap<>(); + String identifier; + if (reqMap.get("identifier") != null) identifier = reqMap.get("identifier"); + else { + respMap.put("status", "0"); + respMap.put("response", "identifier not found"); + return respMap; + } + try { + reqMap.remove("action"); + reqMap.remove("hrType"); + String handleRecord = JsonUtil.toJson(reqMap); + if (db.get(identifier.getBytes()) == null) { + String idType; + if ((identifier.split("/")).length > 2) + idType = identifier.split("/")[1].split("\\.")[0]; + else + idType = identifier.split("\\.")[0]; + switch (idType) { + case "do": + ViewHandler.handleRecordCount.doCount++; + break; + case "dou": + ViewHandler.handleRecordCount.userCount++; + break; + case "doip": + ViewHandler.handleRecordCount.repoCount++; + break; + default: + break; + } + } else { + db.delete(identifier.getBytes()); + } + + db.put(identifier.getBytes(), handleRecord.getBytes()); + System.out.println("successful update do, identifier: " + identifier); + respMap.put("identifier", identifier); + respMap.put("status", "1"); + respMap.put("response", "reRegister DO success, identifier: " + identifier); + } catch (Exception e) { + e.printStackTrace(); + respMap.put("status", "0"); + respMap.put("response", "unregister failed: " + e.getMessage()); + } + return respMap; + } + + public Map handleUnRegister(Map reqMap) { + Map respMap = new HashMap<>(); + String identifier; + if (reqMap.get("identifier") != null) identifier = reqMap.get("identifier"); + else { + respMap.put("status", "0"); + respMap.put("response", "identifier not found"); + return respMap; + } + try { + switch (identifier.split("\\.")[2]) { + case "470/do": + ViewHandler.handleRecordCount.doCount--; + break; + case "470/doip": + ViewHandler.handleRecordCount.repoCount--; + break; + case "470/dou": + ViewHandler.handleRecordCount.userCount--; + break; + } + db.delete(identifier.getBytes()); + respMap.put("status", "1"); + respMap.put("response", "success delete: " + identifier); + ViewHandler.addDelete(); + } catch (Exception e) { + e.printStackTrace(); + respMap.put("status", "0"); + respMap.put("response", "unregister failed: " + e.getMessage()); + } + return respMap; + } + + public Map handleResolve(Map reqMap) { + Map respMap = new HashMap<>(); + String identifier; + if (reqMap.get("identifier") != null) identifier = reqMap.get("identifier"); + else { + respMap.put("status", "0"); + respMap.put("response", "identifier not found"); + return respMap; + } + try { + if (db.get(identifier.getBytes()) == null) { + respMap.put("status", "0"); + respMap.put("response", "not exist"); + return respMap; + } + String result = new String(db.get(identifier.getBytes())); + respMap = JsonUtil.fromJson(result, HashMap.class); + ViewHandler.addResolve(); + } catch (Exception e) { + e.printStackTrace(); + respMap.put("status", "0"); + respMap.put("response", "resolve failed: " + e.getMessage()); + } + return respMap; + } + + public String geneRandomID() { + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 10; i++) { + int number = random.nextInt(62); + sb.append(str.charAt(number)); + } + return sb.toString(); + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + Map respMap; + try { + //获得表单提交数据(post) + String postString = IOUtils.toString(exchange.getRequestBody()); + Map postInfo = formData2Dic(postString); + respMap = HandleRequest(postInfo); + exchange.sendResponseHeaders(200, 0); + OutputStream os = exchange.getResponseBody(); + os.write(JsonUtil.toJson(respMap).getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public Map formData2Dic(String formData) { + Map result = new HashMap<>(); + if (formData == null || formData.trim().length() == 0) { + return result; + } + final String[] items = formData.split("&"); + Arrays.stream(items).forEach(item -> { + final String[] keyAndVal = item.split("="); + if (keyAndVal.length == 2) { + try { + final String key = URLDecoder.decode(keyAndVal[0], "utf8"); + final String val = URLDecoder.decode(keyAndVal[1], "utf8"); + result.put(key, val); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + }); + return result; + } +} diff --git a/src/main/java/org/bdware/server/irp/StatisticsByDay.java b/src/main/java/org/bdware/server/irp/StatisticsByDay.java new file mode 100644 index 0000000..967d326 --- /dev/null +++ b/src/main/java/org/bdware/server/irp/StatisticsByDay.java @@ -0,0 +1,11 @@ +package org.bdware.server.irp; + +public class StatisticsByDay { + int resolveStatics; + int deleteStatics; + int registerStatics; + + public StatisticsByDay(){ + registerStatics = resolveStatics = deleteStatics = 0; + } +} diff --git a/src/main/java/org/bdware/server/irp/ViewHandler.java b/src/main/java/org/bdware/server/irp/ViewHandler.java new file mode 100644 index 0000000..ef914ad --- /dev/null +++ b/src/main/java/org/bdware/server/irp/ViewHandler.java @@ -0,0 +1,257 @@ +package org.bdware.server.irp; + +import com.google.gson.reflect.TypeToken; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.rocksdb.ReadOptions; +import org.rocksdb.RocksDB; +import org.rocksdb.RocksDBException; +import org.rocksdb.RocksIterator; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class ViewHandler implements HttpHandler { + public static final String STATISTICS_KEY = "STATISTICS"; + public static final String COUNT_KEY = "COUNT"; + private static final Logger LOGGER = LogManager.getLogger(ViewHandler.class); + public static HashMap lhsStatics = new HashMap<>(); + public static HandleRecordCount handleRecordCount; + RocksDB db; + + public ViewHandler(RocksDB rocksDB) throws RocksDBException { + db = rocksDB; + loadFromDB(); + NodeCenterServer.scheduledThreadPool.scheduleWithFixedDelay( + () -> { + try { + saveToDB(); + } catch (RocksDBException e) { + LOGGER.error("saving to DB failed! " + e.getMessage()); + } + }, + 0, + 1, + TimeUnit.SECONDS); + } + + public static String getToday() { + SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); + Date date = new Date(System.currentTimeMillis()); + return formatter.format(date); + } + + public static void addResolve() { + String todayDate = ViewHandler.getToday(); + if (lhsStatics.get(todayDate) == null) { + StatisticsByDay statisticsByDay = new StatisticsByDay(); + statisticsByDay.resolveStatics++; + lhsStatics.put(todayDate, statisticsByDay); + } else { + lhsStatics.get(todayDate).resolveStatics++; + } + } + + public static void addRegister() { + String todayDate = ViewHandler.getToday(); + if (lhsStatics.get(todayDate) == null) { + StatisticsByDay statisticsByDay = new StatisticsByDay(); + statisticsByDay.registerStatics++; + lhsStatics.put(todayDate, statisticsByDay); + } else { + lhsStatics.get(todayDate).registerStatics++; + } + } + + public static void addDelete() { + String todayDate = ViewHandler.getToday(); + if (lhsStatics.get(todayDate) == null) { + StatisticsByDay statisticsByDay = new StatisticsByDay(); + statisticsByDay.deleteStatics++; + lhsStatics.put(todayDate, statisticsByDay); + } else { + lhsStatics.get(todayDate).deleteStatics++; + } + } + + @Override + public void handle(HttpExchange httpExchange) throws IOException { + Map respMap; + try { + //获得表单提交数据(post) + String postString = httpExchange.getRequestURI().getQuery(); + Map params = formData2Dic(postString); + respMap = HandleRequest(params); + httpExchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*"); + httpExchange.sendResponseHeaders(200, 0); + OutputStream os = httpExchange.getResponseBody(); + os.write(JsonUtil.toJson(respMap).getBytes()); + os.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Map HandleRequest(Map postInfo) { + Map respMap = new HashMap<>(); + String action = postInfo.get("action"); + switch (action) { + case "count": + respMap = getLHSCount(); + break; + case "statistics": + respMap = getLHSStatics(postInfo); + break; + default: + System.out.println("error response"); + } + return respMap; + } + + private void loadFromDB() throws RocksDBException { + byte[] statisticBytes = db.get(STATISTICS_KEY.getBytes()); + if (statisticBytes == null) { + lhsStatics = new HashMap<>(); + } else { + lhsStatics = JsonUtil.fromJson(new String(statisticBytes), new TypeToken>() { + }.getType()); + } + byte[] countBytes = db.get(COUNT_KEY.getBytes()); + if (countBytes == null) { + handleRecordCount = new HandleRecordCount(); + handleRecordCount.doCount = handleRecordCount.repoCount = handleRecordCount.userCount = 0; + } else { + handleRecordCount = JsonUtil.fromJson(new String(countBytes), HandleRecordCount.class); + } + } + + private void saveToDB() throws RocksDBException { + String statisticStr = JsonUtil.toJson(lhsStatics); + db.put(STATISTICS_KEY.getBytes(), statisticStr.getBytes()); + String countStr = JsonUtil.toJson(handleRecordCount); + db.put(COUNT_KEY.getBytes(), countStr.getBytes()); + } + + public Map getLHSStatics(Map postInfo) { + if (postInfo.containsKey("day")) { + return getLHSStaticsWithTime(postInfo); + } + HashMap respMap = new HashMap<>(); + for (String key : lhsStatics.keySet()) { + respMap.put(key + "", JsonUtil.toJson(lhsStatics.get(key))); + } + return respMap; + } + + public Map getLHSStaticsWithTime(Map postInfo) { + long day = Long.parseLong(postInfo.get("day")); + LOGGER.info(day); + List resolve = new ArrayList<>(); + List delete = new ArrayList<>(); + List register = new ArrayList<>(); + + for (int i = 0; i < day; i++) { + resolve.add(0); + delete.add(0); + register.add(0); + } + + long interval = 24 * 60 * 60 * 1000; //一天 + long cur = System.currentTimeMillis(); + long startTime = cur - day * interval; + // logger.info(JsonUtil.toJson(lhsStatics)); + try { + for (String k : lhsStatics.keySet()) { + long time = new SimpleDateFormat("yyyyMMdd").parse(k).getTime(); + if (time < startTime || time > cur) { + continue; + } + int index = (int) ((time - startTime) / interval); + //logger.info(index); + resolve.set(index, resolve.get(index) + lhsStatics.get(k).resolveStatics); + delete.set(index, delete.get(index) + lhsStatics.get(k).deleteStatics); + register.set(index, register.get(index) + lhsStatics.get(k).registerStatics); + } + } catch (ParseException e) { + e.printStackTrace(); + } + + HashMap respMap = new HashMap<>(); + respMap.put("resolve", resolve); + respMap.put("delete", delete); + respMap.put("register", register); + + return respMap; + } + + public Map getLHSCount() { + HashMap respMap = new HashMap<>(); + respMap.put("repoCount", String.valueOf(handleRecordCount.repoCount)); + respMap.put("doCount", String.valueOf(handleRecordCount.doCount)); + + ReadOptions readOptions = new ReadOptions().setPrefixSameAsStart(true); + RocksIterator it1 = db.newIterator(readOptions); + + ArrayList doIDList = new ArrayList<>(); + it1.seek("86.5000.470/do.".getBytes()); + int total = 0; + while (it1.isValid() && total < 10) { + doIDList.add(new String(it1.key())); + total++; + it1.next(); + } + it1.close(); + + RocksIterator it2 = db.newIterator(readOptions); + ArrayList repoIDList = new ArrayList<>(); + it2.seek("86.5000.470/doip.".getBytes()); + total = 0; + while (it2.isValid() && total < 10) { + repoIDList.add(new String(it2.key())); + total++; + it2.next(); + } + + respMap.put("repoIDList", repoIDList); + respMap.put("doIDList", doIDList); + return respMap; + + } + + public Map formData2Dic(String formData) { + Map result = new HashMap<>(); + if (formData == null || formData.trim().length() == 0) { + return result; + } + final String[] items = formData.split("&"); + Arrays.stream(items).forEach(item -> { + final String[] keyAndVal = item.split("="); + if (keyAndVal.length == 2) { + try { + final String key = URLDecoder.decode(keyAndVal[0], "utf8"); + final String val = URLDecoder.decode(keyAndVal[1], "utf8"); + result.put(key, val); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + }); + return result; + } + + static class HandleRecordCount { + int repoCount; + int doCount; + int userCount; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/CMNode.java b/src/main/java/org/bdware/server/nodecenter/CMNode.java new file mode 100644 index 0000000..035bc29 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/CMNode.java @@ -0,0 +1,146 @@ +package org.bdware.server.nodecenter; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.db.KeyValueDBUtil; + +import java.util.List; + +public class CMNode { + private static final Logger LOGGER = LogManager.getLogger(CMNode.class); + // public List contracts; + public volatile List contracts; // 有路由信息 + public transient NodeCenterActions connection; + public String pubKey; + public String nodeName; + public String udpID; + public String ipPort; + public int events; + public String cimanager = ""; + public String peerID; + public String masterAddress; // 这个节点如果作为master的地址,例如18010的这个值就是18011 + public int contractVersion; + + public CMNode(NodeCenterActions nodeCenterController, String pubKey) { + connection = nodeCenterController; + this.pubKey = pubKey; + } + + // TODO nodeMangerPubkey + public void updateContract(List cons, int version) { + contracts = cons; + contractVersion = version; + for (ContractDesp desp : cons) { + KeyValueDBUtil.instance.setValue( + NCTables.ContractMeta.toString(), desp.contractID, desp.contractName); + } + } + + public void sendEMsg(String msg) { + connection.controller.sendEMsg(msg); + } + + public String formatContractName(String contractIDOrName) { + if (null != contracts) { + for (ContractDesp desp : contracts) { + if (desp.contractID.equals(contractIDOrName) + || desp.contractName.equals(contractIDOrName)) { + return desp.contractName; + } + } + } + return null; + } + + public boolean containsContract(String contractIDOrName) { + if (null != contracts) { + for (ContractDesp desp : contracts) { + if (desp.contractID.equals(contractIDOrName) + || desp.contractName.equals(contractIDOrName)) return true; + } + } + return false; + } + + public boolean containsEvent(String contractIDOrName, String event) { + if (null != contracts) { + for (ContractDesp desp : contracts) { + if (desp.contractID.equals(contractIDOrName) + || desp.contractName.equals(contractIDOrName)) { + if (desp.events.containsKey(event)) { + return true; + } + } + } + } + return false; + } + + public boolean checkAlive() { + if (null == connection) { + return false; + } + try { + if (!connection.controller.isOpen()) { + LOGGER.info("node " + nodeName + "(" + pubKey.substring(0, 5) + ") may be offline!"); +// System.out.println( +// new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") +// .format(new Date(System.currentTimeMillis())) +// + "[ADSP][CMNode] isAlive : " +// + nodeName +// + " not open!" +// + "\n"); + return doubleCheckAlive(); + } else { + return true; + } + } catch (Exception e) { + LOGGER.error("checking alive failed! " + e.getMessage()); + } + return false; + } + + private boolean doubleCheckAlive() { + try { + // Thread.sleep(3000L); + Thread.sleep(500L); + if (connection.controller.isOpen()) { + LOGGER.info("node " + nodeName + "(" + pubKey.substring(0, 5) + ") is online!"); + return true; + } + } catch (Exception e) { + LOGGER.error("checking alive failed! " + e.getMessage()); + } + LOGGER.info("node " + nodeName + "(" + pubKey.substring(0, 5) + ") may be offline!"); + return false; + } + + public void removeCIManager(String string) { + LOGGER.debug("removeCIManager" + string); + int start = this.cimanager.indexOf(string); + if (start > 0) { + this.cimanager = + this.cimanager + .substring(0, start) + .concat(this.cimanager.substring(start + 130)); + } + } + + public void addCIManager(String string) { + LOGGER.debug("addCIManager" + string); + this.cimanager = this.cimanager.concat(" " + string); + } + + public void setCIManager(String string) { + this.cimanager = string; + } + + public void setPeerID(String string) { + this.peerID = string; + } + + public void setIpPort(String string) { + this.ipPort = string; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/ContractExecutor.java b/src/main/java/org/bdware/server/nodecenter/ContractExecutor.java new file mode 100644 index 0000000..86a20d8 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/ContractExecutor.java @@ -0,0 +1,7 @@ +package org.bdware.server.nodecenter; + + import org.bdware.sc.conn.ResultCallback; + +public interface ContractExecutor { + public void execute(String requestID, ResultCallback rc, String req); +} diff --git a/src/main/java/org/bdware/server/nodecenter/ElectMasterTimeRecorder.java b/src/main/java/org/bdware/server/nodecenter/ElectMasterTimeRecorder.java new file mode 100644 index 0000000..4b19b3e --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/ElectMasterTimeRecorder.java @@ -0,0 +1,15 @@ +package org.bdware.server.nodecenter; + +import java.util.HashMap; +import java.util.Map; + + +//NC +public class ElectMasterTimeRecorder { + public static Long startElect; //NC开始选举 + public static Long findNewMaster; //NC选出新master + public static String newMaster; + public static Long changeMasterFinish; //新master在NC上更新路由信息 + + public static Map nodeFindMasterCrash = new HashMap(); //nodeID,electMaster调用时间 +} diff --git a/src/main/java/org/bdware/server/nodecenter/LogActions.java b/src/main/java/org/bdware/server/nodecenter/LogActions.java new file mode 100644 index 0000000..e7f28dc --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/LogActions.java @@ -0,0 +1,186 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.db.MultiIndexTimeDBUtilIntf; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.bdware.server.action.Action; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +// CenterPortal的所有日志和统计数据 +public class LogActions { + private static final Logger LOGGER = LogManager.getLogger(LogActions.class); + + NCManagerAction managerAction; + + public LogActions(NCManagerAction managerAction) { + this.managerAction = managerAction; + } + + private void queryInternal( + String actResp, + MultiIndexTimeDBUtilIntf db, + JsonObject json, + ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + Map ret = new HashMap<>(); + if (!json.has("start")) { + ret.put("action", actResp); + ret.put("data", new ArrayList<>()); + resultCallback.onResult(JsonUtil.toJson(ret)); + return; + } + long startTime = json.get("start").getAsLong(); + long endTime = System.currentTimeMillis(); + if (json.has("end")) { + endTime = json.get("end").getAsLong(); + } + String[] category = new String[]{null}; + if (json.has("category")) { + category = json.get("category").getAsString().split(","); + } + Map data = new HashMap<>(); + for (String str : category) { + List array = db.queryByDateAsJson(str, startTime, endTime); + if (str == null) data.put("primary", array); + else data.put(str, array); + } + ret.put("action", actResp); + ret.put("data", data); + if (json.has("requestID")) { + ret.put("responseID", json.get("requestID")); + } + resultCallback.onResult(JsonUtil.toJson(ret)); + long end = System.currentTimeMillis(); + LOGGER.debug("[queryInternal:time]" + (end - start)); + } + + private void countLogByCategoryInternal( + String actResp, + MultiIndexTimeDBUtilIntf db, + JsonObject json, + ResultCallback resultCallback) { + Map ret = new HashMap<>(); + if (!json.has("start")) { + ret.put("action", actResp); + ret.put("data", new ArrayList<>()); + resultCallback.onResult(JsonUtil.toJson(ret)); + return; + } + long startTime = json.get("start").getAsLong(); + long endTime = System.currentTimeMillis(); + if (json.has("end")) { + endTime = json.get("end").getAsLong(); + } + long interval = json.get("interval").getAsLong(); + String[] category = new String[]{null}; + if (json.has("category")) { + category = json.get("category").getAsString().split(","); + } + JsonObject data = new JsonObject(); + for (String str : category) { + JsonArray array = db.countInInterval(str, startTime, interval, endTime); + if (str == null) data.add("primary", array); + else data.add(str, array); + } + ret.put("action", actResp); + ret.put("data", data); + if (json.has("requestID")) { + ret.put("responseID", json.get("requestID")); + } + resultCallback.onResult(JsonUtil.toJson(ret)); + } + + @Action(userPermission = 1 << 8, async = true) + public void queryActionLog(JsonObject json, ResultCallback resultCallback) { + queryInternal("onQueryActionLog", NodeCenterServer.nodeHttpLogDB, json, resultCallback); + } + + @Action(userPermission = 1 << 8, async = true) + public void countActionLogByCategory(JsonObject json, ResultCallback resultCallback) { + countLogByCategoryInternal( + "onCountActionLogByCategory", NodeCenterServer.nodeHttpLogDB, json, resultCallback); + } + + @Action(userPermission = 1 << 8, async = true) + public void queryCMLog(JsonObject json, ResultCallback resultCallback) { + queryInternal("onQueryCMLog", NodeCenterServer.nodeTcpLogDB, json, resultCallback); + } + + @Action(userPermission = 1 << 8, async = true) + public void countCMLogByCategory(JsonObject json, ResultCallback resultCallback) { + countLogByCategoryInternal( + "onCountCMLogByCategory", NodeCenterServer.nodeTcpLogDB, json, resultCallback); + } + + @Action(userPermission = 1 << 9) + public void queryUserStat(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + long userList = KeyValueDBUtil.instance.getCount(NCTables.NodeUser.toString()); + long applylist = KeyValueDBUtil.instance.getCount(NCTables.ApplyRole.toString()); + resultCallback.onResult( + "{\"action\":\"onQueryUserStat\",\"userListCount\":" + + userList + + ",\"applyListCount\":" + + applylist + + "}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[queryUserStat:time]" + (end - start)); + } + + @Action(userPermission = 1 << 9, async = true) + public void listNodes(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + final String pubKey = managerAction.pubKey; + LOGGER.debug("[listNodes] managerAction.pubKey " + pubKey); + Map nodeinfos = NodeCenterActions.nodeInfos; // 所有在线节点? + final Map cinodeinfos = new HashMap<>(); + List dbnodes = KeyValueDBUtil.instance.getKeys(NCTables.NodesDB.toString()); + List onlineNodes = new ArrayList<>(); + + Map ret = new HashMap<>(); + if (KeyValueDBUtil.instance + .getValue(NCTables.ConfigDB.toString(), "__CenterManager__") + .contains(pubKey)) { + LOGGER.debug("is center manager"); + LOGGER.debug("dbnodes " + dbnodes.toString()); + for (CMNode node : nodeinfos.values()) { + LOGGER.debug("Offline node " + node.nodeName + node.pubKey); + if (dbnodes.contains(node.pubKey)) { + dbnodes.remove(node.pubKey); + LOGGER.debug("Delete Online node " + node.pubKey); + onlineNodes.add(node); + } + } + ret.put("action", "onListNodes"); + ret.put("online", onlineNodes); + ret.put("offline", dbnodes); + resultCallback.onResult(JsonUtil.toJson(ret)); + return; + } + for (Map.Entry entry : nodeinfos.entrySet()) { + LOGGER.debug("Key = " + entry.getKey() + ", Value = " + entry.getValue()); + String cimanager = entry.getValue().cimanager; + if (!cimanager.contains(pubKey)) { + cinodeinfos.put(entry.getKey(), entry.getValue()); + } + } + ret.put("action", "onListNodes"); + ret.put("online", cinodeinfos); + ret.put("offline", new ArrayList()); + // 合约管理员看不到offline nodes + resultCallback.onResult(JsonUtil.toJson(ret)); + long end = System.currentTimeMillis(); + LOGGER.debug("[listNodes:time]" + (end - start)); + } + +} diff --git a/src/main/java/org/bdware/server/nodecenter/MasterActions.java b/src/main/java/org/bdware/server/nodecenter/MasterActions.java new file mode 100644 index 0000000..3e7a133 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/MasterActions.java @@ -0,0 +1,289 @@ +package org.bdware.server.nodecenter; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.units.RequestCache; +import org.bdware.server.NodeCenterServer; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class MasterActions { + private static final Logger LOGGER = LogManager.getLogger(MasterActions.class); + // TODO 定期清缓存 + public static Map requestCache = + new ConcurrentHashMap<>(); // key is contractID,only for requestAll type contract + + static { + NodeCenterServer.scheduledThreadPool.scheduleWithFixedDelay( + () -> { + boolean flag = clearCache(); + if (flag) { + try { + Thread.sleep(10000L); + } catch (InterruptedException e) { + LOGGER.error("sleeping is interrupted! " + e.getMessage()); + } + } + }, + 0, + 0, + TimeUnit.SECONDS); + } + + public NodeCenterFrameHandler controller; + + public MasterActions(NodeCenterFrameHandler nodeCenterFrameHandler) { + controller = nodeCenterFrameHandler; + } + + // judge the just online node whwther need to restart contracts,send the contracts'info + /* public static void restartContracts(String nodePubKey){ + if(!NodeCenterActions.recoverMap.containsKey(nodePubKey)){ + return; + } + + System.out.println("开始恢复节点" + NodeCenterActions.nodeinfos.get(nodePubKey).nodeName); + + + // 恢复该节点的每一个集群运行的合约 + for (ContractRecord record : NodeCenterActions.recoverMap.get(nodePubKey).values()) { + String contractID = record.contractID; + if (record.recoverFlag != RecoverFlag.ToRecover) + continue; + + Map req = new HashMap(); + req.put("action", "queryUnitStatus"); + req.put("contractID", contractID); + CMNode cmNode = NodeCenterActions.nodeinfos.get(nodePubKey); + cmNode.connection.controller.sendMsg(gson.toJson(req)); + } + }*/ + + /* @Action(async = true) + public void onQueryUnitStatus(Map args, final ResultCallback rc) { + String mode = args.get("mode"); + String nodeID = args.get("nodeID"); + String contractID = args.get("contractID"); + ContractRecord record = NodeCenterActions.recoverMap.get(nodeID).get(contractID); + logger.debug("节点" + NodeCenterActions.nodeinfos.get(args.get("nodeID")).nodeName + "崩溃前模式为" + mode); + if(mode.equals(ContractUnitStatus.CommonMode.toString())){ + restartFromCommonMode(nodeID,record); + }else if(mode.equals(ContractUnitStatus.StableMode.toString())){ + restartFromStableMode(nodeID,record); + } + }*/ + + // 当StableMode的节点的lastExeSeq和集群相差超过10时,通过CommonNode的恢复方式恢复 + /* @Action(async = true) + public void restartByCommonNode(Map args, final ResultCallback rc){ + String nodeID = args.get("nodeID"); + String contractID = args.get("contractID"); + ContractRecord record = NodeCenterActions.recoverMap.get(nodeID).get(contractID); + restartFromCommonMode(nodeID,record); + }*/ + + /* + public static void restartFromCommonMode(String nodePubKey,ContractRecord record){ + logger.debug("从CommonMode中恢复:"); + + String contractID = record.contractID; + + record.recoverFlag = RecoverFlag.Recovering; + //先发消息,让恢复节点的该合约收到消息后只加入队列不dealRequests + Map request = new HashMap<>(); + request.put("action", "setRecovering"); + request.put("contractID",contractID); + CMNode node = NodeCenterActions.nodeinfos.get(nodePubKey); + node.connection.controller.sendMsg(gson.toJson(request)); + + //System.out.println("第一步 : [NodeCeterActions] restartContracts 开始处理合约 contractID=" + contractID); + + if (NodeCenterActions.contractID2Members.containsKey(contractID)) { + + // 在nodeinfos中找节点,该节点的pubKey在pubKeys中 + MultiPointContractInfo info = NodeCenterActions.contractID2Members.get(contractID); + CMNode cmNode = null; + for (int i = 0; i < info.members.size(); i++) { + int size = info.members.size(); + String tempNodeID = info.members.get(record.order.incrementAndGet() % size); + + if(NodeCenterActions.nodeinfos.containsKey(tempNodeID)) + cmNode = NodeCenterActions.nodeinfos.get(tempNodeID); + else continue; + + //System.out.println("查询节点 " + cmNode.nodeName); + + if (cmNode != null && !cmNode.pubKey.equals(nodePubKey)) { + //System.out.println("第二步 : [NodeCenterActions] 找到一个依赖恢复节点,其节点名为 " + cmNode.nodeName); + + Map req = new HashMap(); + req.put("action", "dumpCurrentState"); + req.put("contractID", contractID); + req.put("targetNodePubkey", nodePubKey); + + // NC向该节点发送请求,让其存储自身当前状态并发给NC + cmNode.connection.controller.sendMsg(gson.toJson(req)); + + return; + } + } + + if(cmNode == null){ + logger.debug("[NodeCenterActions] Can't find a recover rely node!"); + } + } + } + */ + + // TODO + /* public static void restartFromStableMode(String nodePubkey,ContractRecord record){ + logger.debug("从StableMode中恢复:"); + + String contractID = record.contractID; + record.recoverFlag = RecoverFlag.Recovering; + //先发消息,让恢复节点的该合约收到消息后只加入队列不dealRequests + Map request = new HashMap<>(); + request.put("action", "setRecovering"); + request.put("contractID",contractID); + request.put("mode", ContractUnitStatus.StableMode.toString()); + int lastExeSeq = NodeCenterActions.contractID2Members.get(contractID).ai.get() - 1; + request.put("lastExeSeq",lastExeSeq + ""); + CMNode node = NodeCenterActions.nodeinfos.get(nodePubkey); + node.connection.controller.sendMsg(gson.toJson(request)); + }*/ + + /* public static void unitModeCheck(String contractID){ + MultiPointContractInfo mpci = NodeCenterActions.contractID2Members.get(contractID); + int total = 0,online = 0; + for(String nodeId : mpci.members){ + if(NodeCenterActions.nodeinfos.containsKey(nodeId)){ + online++; + } + total++; + } + + logger.debug("合约" + contractID + "的集群,上线节点有" + online + "个,总共节点有" + total + "个. Math.ceil(total / 2)=" + Math.ceil((double)total / 2) + " online > Math.ceil(total / 2)" + (online > Math.ceil(total / 2)) + " mpci.unitStatus=" + mpci.unitStatus); + if(online > Math.ceil((double)total / 2) && mpci.unitStatus == ContractUnitStatus.StableMode){ + logger.debug("合约" + contractID + "的集群更改模式为" + ContractUnitStatus.CommonMode.toString()); + + Map req = new HashMap(); + req.put("action", "changeUnitStatus"); + req.put("contractID", contractID); + req.put("mode",ContractUnitStatus.CommonMode.toString()); + for(String nodeId : mpci.members){ + if(NodeCenterActions.nodeinfos.containsKey(nodeId)){ + CMNode cmNode = NodeCenterActions.nodeinfos.get(nodeId); + logger.debug("发消息给节点 " + cmNode.nodeName + " 设置合约" + contractID + "的集群模式为CommonMode"); + cmNode.connection.controller.sendMsg(gson.toJson(req)); + } + } + + mpci.unitStatus = ContractUnitStatus.CommonMode; + }else if(online <= Math.ceil((double)total / 2) && mpci.unitStatus == ContractUnitStatus.CommonMode){ + logger.debug("合约" + contractID + "的集群更改模式为" + ContractUnitStatus.StableMode.toString()); + + Map req = new HashMap(); + req.put("action", "changeUnitStatus"); + req.put("contractID", contractID); + req.put("mode",ContractUnitStatus.StableMode.toString()); + for(String nodeId : mpci.members){ + if(NodeCenterActions.nodeinfos.containsKey(nodeId)){ + CMNode cmNode = NodeCenterActions.nodeinfos.get(nodeId); + Map map = NodeCenterActions.recoverMap.get(nodeId).get(contractID).members; + req.put("membersStr",JsonUtil.toJson(map)); //ContractRecord's members + logger.debug("发消息给节点 " + cmNode.nodeName + " 设置合约" + contractID + "的集群模式为StableMode"); + cmNode.connection.controller.sendMsg(gson.toJson(req)); + } + } + + mpci.unitStatus = ContractUnitStatus.StableMode; + + //将ContractRecord中members发给集群中节点 + + } + }*/ + + /* @Action(async = true) + public void recoverFinish(Map args, final ResultCallback rc) { + ContractRecord cr = NodeCenterActions.recoverMap.get(args.get("nodeID")).get(args.get("contractID")); + logger.debug("节点" + NodeCenterActions.nodeinfos.get(args.get("nodeID")).nodeName + "恢复完成!"); + if(cr.recoverFlag == RecoverFlag.Recovering) + cr.recoverFlag = RecoverFlag.Fine; + + logger.debug("恢复完成,需要检查合约" + args.get("contractID") + "的集群运行模式"); + unitModeCheck(args.get("contractID")); + }*/ + + static boolean clearCache() { + if (requestCache.isEmpty()) return true; + + // final long time = System.currentTimeMillis() - 60000L; //60s + // requestCache.entrySet() + // .removeIf( + // entry -> { + // RequestCache cache = entry.getValue(); + // if (cache == null) return true; + // return cache.getTime() < time; + // }); + // + // return false; + + // 对每个合约,只保存最近RESERVED个请求 + // for(RequestCache rc : requestCache.values()){ + // int delete = rc.size() - RequestCache.RESERVED,count = 0; + // if(delete > 0){ + // synchronized (rc){ + // for(Iterator iterator = rc.requests.keySet().iterator(); + // iterator.hasNext(); ) { + // Integer key = iterator.next(); + // if(count < delete){ + // iterator.remove(); + // count++; + // } + // } + // } + // } + // } + + return false; + } + + static RequestCache getCache(String contractID) { + if (contractID != null) { + RequestCache cache = requestCache.get(contractID); + if (cache != null) return cache; + else { + LOGGER.debug("[NodeCenterActions] create requestcache:" + contractID); + RequestCache reqc = new RequestCache(); + requestCache.put(contractID, reqc); + return reqc; + } + } + return null; + } + + /* @Action(async = true) + public void sendCachedRequests(Map args, final ResultCallback rc) { + String contractID = args.get("contractID"); + int start = Integer.parseInt(args.get("start")); + int end = Integer.parseInt(args.get("end")); + + RequestCache cache = getCache(contractID); + for(int i = start + 1;i < end;i++){ + if(cache == null || !cache.containsKey(i)){ + //this node crash + String nodeID = args.get("nodeID"); + MasterActions.restartContracts(nodeID); + } + JsonObject jo = cache.get(i); + if(jo == null){ + logger.debug("在NC中第 " + i + "个请求已经被清,无法发给恢复节点!"); + }else + controller.sendMsg(JsonUtil.toJson(jo)); + + logger.debug("NC发送第 " + i + " " + jo.get("seq").getAsString() + " 个请求给Node"); + } + }*/ +} diff --git a/src/main/java/org/bdware/server/nodecenter/MetaIndexAction.java b/src/main/java/org/bdware/server/nodecenter/MetaIndexAction.java new file mode 100644 index 0000000..5b8663b --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/MetaIndexAction.java @@ -0,0 +1,553 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.CharArraySet; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.WordlistLoader; +import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.*; +import org.apache.lucene.queryparser.classic.ParseException; +import org.apache.lucene.queryparser.classic.QueryParser; +import org.apache.lucene.search.*; +import org.apache.lucene.store.FSDirectory; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.Action; +import org.bdware.server.nodecenter.searchresult.ContractMeta; +import org.bdware.server.nodecenter.searchresult.ResultModel; +import org.wltea.analyzer.lucene.IKAnalyzer; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class MetaIndexAction { // public static IndexWriter indexWriter; + private static final Logger LOGGER = LogManager.getLogger(MetaIndexAction.class); + // public static IndexSearcher indexSearcher; + // public static TopDocs topDocs; + // public static Query query; + // public static QueryParser queryParser; + // public static Analyzer analyzer; + // public static IndexWriterConfig config; + // public static List docList = new ArrayList<>(); + public static FSDirectory indexDir; + public static Integer PAGE_SIZE = 10; + public static Integer page; + public static boolean isEmpty = false; + public static NodeCenterFrameHandler controller; + // public MetaIndexAction(NodeCenterFrameHandler nodeCenterFrameHandler) { + // controller = nodeCenterFrameHandler; + // } + private static IndexWriter indexWriter; + + static { + initIndex(); + } + + private static void initIndex() { + try { + File dir = new File("./NodeCenterDB/MetaIndex"); + if (!dir.exists()) { + LOGGER.info("make metaIndex dir "); + dir.mkdirs(); + isEmpty = true; + } + indexDir = FSDirectory.open(Paths.get(dir.toURI())); + // Analyzer analyzer = new StandardAnalyzer(); + Analyzer analyzer = new IKAnalyzer(); + IndexWriterConfig config = new IndexWriterConfig(analyzer); + indexWriter = new IndexWriter(indexDir, config); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void updateContractsIndex(List contracts, ResultCallback rc) + throws IOException { + IndexReader indexReader = null; + for (ContractDesp thisDesp : contracts) { + JsonObject req = new JsonObject(); + if (!isEmpty) { + try { + indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + LOGGER.debug(thisDesp.contractName + "--> try to index"); + Query query = new TermQuery(new Term("contractID", thisDesp.contractID)); + TopDocs docs = indexSearcher.search(query, 10); + LOGGER.debug(docs.scoreDocs); + if (null != thisDesp.contractName && (docs.scoreDocs == null || docs.scoreDocs.length == 0)) { + req.addProperty("action", "requestReadMe"); + req.addProperty("contractID", thisDesp.contractID); + rc.onResult(req.toString()); + LOGGER.info("contract " + thisDesp.contractName + " --> actually to index"); + continue; + } + } catch (Exception e) { + LOGGER.warn("getting index failed! " + e.getMessage()); + } + } + req.addProperty("action", "requestReadMe"); + req.addProperty("contractID", thisDesp.contractID); + rc.onResult(req.toString()); + LOGGER.info("contract " + thisDesp.contractName + " --> actually to index"); + } + if (null != indexReader) { + indexReader.close(); + } + } + + public static String accuSearch(String keyword) { + try { + DirectoryReader indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + Term t = new Term("contractID", keyword); + Query query = new TermQuery(t); + TopDocs docs = indexSearcher.search(query, 10); + return docs.scoreDocs.length + ""; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + public static void delIndexbyCID(JsonObject jo) { + try { + if (!jo.has("contractID")) return; + String contractID = jo.get("contractID").getAsString(); + LOGGER.info("contractID:" + contractID + "-->actually to delete"); + indexWriter.deleteDocuments(new Term("contractID", contractID)); + indexWriter.commit(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void delIndexbyOwner(JsonObject jo) { + try { + if (!jo.has("owner")) return; + String owner = jo.get("owner").getAsString(); + LOGGER.info("owner:" + owner + "-->actually to delete"); + indexWriter.deleteDocuments(new Term("owner", owner)); + indexWriter.commit(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String search(JsonObject searchReq) { + try { + if (!searchReq.has("searchType")) { + return "missing arguments"; + } + switch (searchReq.get("searchType").getAsString()) { + case "getMetabyCID": + return getMetabyCID(searchReq); + case "getMetabyOwner": + return getMetabyOwner(searchReq); + case "getMetabyPubkey": + return getMetabyPubkey(searchReq); + case "getMetabyReadme": + return getMetabyReadme(searchReq); + default: + return "no such search type"; + } + // if (searchReq != null) { + // logger.info(searchReq.get("pubkey").getAsString() + "---->start + // search"); + // DirectoryReader indexReader = DirectoryReader.open(indexDir); + // IndexSearcher indexSearcher = new IndexSearcher(indexReader); + // BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder(); + // reandler(searchReq, queryBuilder); + // BooleanQuery searchQuery = queryBuilder.build(); + // TopDocs docs = indexSearcher.search(searchQuery, 10); + // return docs.scoreDocs.length + ""; + // } + // else return "0"; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + public static String getMetabyReadme(JsonObject jo) { + try { + if (!jo.has("keyword")) return "missing arguments: keyword"; + + if (!jo.has("page")) page = 1; + else page = jo.get("page").getAsInt(); + if (page <= 0) page = 1; + if (jo.has("pageSize")) PAGE_SIZE = jo.get("pageSize").getAsInt(); + String keyword = jo.get("keyword").getAsString(); + // Analyzer analyzer = new StandardAnalyzer(); + Analyzer analyzer = new IKAnalyzer(); + QueryParser queryParser = new QueryParser("readmeStr", analyzer); + Query rmQuery = queryParser.parse(keyword); + + DirectoryReader indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + TopDocs docs = indexSearcher.search(rmQuery, 1000); + // Document document = null ; + // if (docs.scoreDocs!=null&& docs.scoreDocs.length>0) { + // document = indexSearcher.doc(docs.scoreDocs[0].doc); + // ContractMeta contractMeta = new ContractMeta(); + // contractMeta.setContractID(document.get("contractID")); + // contractMeta.setOwner(document.get("owner")); + // contractMeta.setPubkey(document.get("pubkey")); + // contractMeta.setReadmeStr(document.get("readmeStr")); + // String rs = JsonUtil.toJson(contractMeta); + // return rs; + // } + ResultModel resultModel = null; + if (docs.scoreDocs != null && docs.scoreDocs.length > 0) + resultModel = paginate(docs, indexReader); + if (resultModel != null) { + String rs = JsonUtil.toJson(resultModel); + return rs; + } + return "not found"; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + private static String getMetabyPubkey(JsonObject jo) { + try { + if (!jo.has("pubkey")) return "missing arguments: pubkey"; + + if (!jo.has("page")) page = 1; + else page = jo.get("page").getAsInt(); + if (page <= 0) page = 1; + + String pubkey = jo.get("pubkey").getAsString(); + DirectoryReader indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + Query query = new TermQuery(new Term("pubkey", pubkey)); + TopDocs docs = indexSearcher.search(query, 10); + // Document document = null; + // if (docs.scoreDocs!=null&& docs.scoreDocs.length>0) { + // document = indexSearcher.doc(docs.scoreDocs[0].doc); + // ContractMeta contractMeta = new ContractMeta(); + // contractMeta.setContractID(document.get("contractID")); + // contractMeta.setOwner(document.get("owner")); + // contractMeta.setPubkey(document.get("pubkey")); + // contractMeta.setReadmeStr(document.get("readmeStr")); + // String rs = JsonUtil.toJson(contractMeta); + // return rs; + // } + ResultModel resultModel = null; + if (docs.scoreDocs != null && docs.scoreDocs.length > 0) + resultModel = paginate(docs, indexReader); + if (resultModel != null) { + String rs = JsonUtil.toJson(resultModel); + return rs; + } + return "not found"; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + private static String getMetabyCID(JsonObject jo) { + System.out.println("getMetabyCID" + jo.toString()); + try { + if (!jo.has("contractID")) return "missing arguments: contractID"; + + if (!jo.has("page")) page = 1; + else page = jo.get("page").getAsInt(); + if (page <= 0) page = 1; + + String contractID = jo.get("contractID").getAsString(); + DirectoryReader indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + Query query = new TermQuery(new Term("contractID", contractID)); + TopDocs docs = indexSearcher.search(query, 10); + // Document document = null; + // if (docs.scoreDocs!=null&& docs.scoreDocs.length>0) { + // document = indexSearcher.doc(docs.scoreDocs[0].doc); + // ContractMeta contractMeta = new ContractMeta(); + // contractMeta.setContractID(document.get("contractID")); + // contractMeta.setOwner(document.get("owner")); + // contractMeta.setPubkey(document.get("pubkey")); + // contractMeta.setReadmeStr(document.get("readmeStr")); + // String rs = JsonUtil.toJson(contractMeta); + // return rs; + // } + ResultModel resultModel = null; + if (docs.scoreDocs != null && docs.scoreDocs.length > 0) + resultModel = paginate(docs, indexReader); + if (resultModel != null) { + String rs = JsonUtil.toJson(resultModel); + return rs; + } + return "not found"; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + private static String getMetabyOwner(JsonObject jo) { + try { + if (!jo.has("owner")) return "missing arguments: owner"; + + if (!jo.has("page")) page = 1; + else page = jo.get("page").getAsInt(); + if (page <= 0) page = 1; + if (jo.has("pageSize")) PAGE_SIZE = jo.get("pageSize").getAsInt(); + String owner = jo.get("owner").getAsString(); + DirectoryReader indexReader = DirectoryReader.open(indexDir); + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + Query query = new TermQuery(new Term("owner", owner)); + TopDocs docs = indexSearcher.search(query, 1000); + // Document document = null; + // if (docs.scoreDocs!=null&& docs.scoreDocs.length>0) { + // document = indexSearcher.doc(docs.scoreDocs[0].doc); + // ContractMeta contractMeta = new ContractMeta(); + // contractMeta.setContractID(document.get("contractID")); + // contractMeta.setOwner(document.get("owner")); + // contractMeta.setPubkey(document.get("pubkey")); + // contractMeta.setReadmeStr(document.get("readmeStr")); + // String rs = JsonUtil.toJson(contractMeta); + // return rs; + // } + ResultModel resultModel = null; + if (docs.scoreDocs != null && docs.scoreDocs.length > 0) + resultModel = paginate(docs, indexReader); + if (resultModel != null) { + String rs = JsonUtil.toJson(resultModel); + return rs; + } + return "not found"; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + return bo.toString(); + } + } + + public static void reqHandler(JsonObject req, BooleanQuery.Builder query) + throws ParseException { + if (req.get("contractID").getAsString() != null) { + query.add( + new FuzzyQuery(new Term("contractID", req.get("contractID").getAsString())), + BooleanClause.Occur.SHOULD); + } + if (req.get("owner").getAsString() != null) { + query.add( + new FuzzyQuery(new Term("owner", req.get("owner").getAsString())), + BooleanClause.Occur.SHOULD); + } + if (req.get("pubkey").getAsString() != null) { + query.add( + new PrefixQuery(new Term("pubkey", req.get("pubkey").getAsString())), + BooleanClause.Occur.SHOULD); + } + if (req.get("readmeStr").getAsString() != null) { + Analyzer analyzer = new StandardAnalyzer(); + QueryParser queryParser = new QueryParser("name", analyzer); + Query rmQuery = queryParser.parse(req.get("readmeStr").getAsString()); + query.add(rmQuery, BooleanClause.Occur.SHOULD); + } + } + + public static ResultModel paginate(TopDocs docs, IndexReader reader) throws IOException { + ResultModel resultModel = new ResultModel(); + Integer start = (page - 1) * PAGE_SIZE; + Integer end = page * PAGE_SIZE; + resultModel.setContractCount(docs.totalHits.value); + ScoreDoc[] scoreDocs = docs.scoreDocs; + List contractMetaList = new ArrayList<>(); + if (scoreDocs != null) { + for (int i = start; i < scoreDocs.length && i < end; i++) { + Document document = reader.document(scoreDocs[i].doc); + ContractMeta contractMeta = new ContractMeta(); + contractMeta.setContractID(document.get("contractID")); + contractMeta.setStatus(document.get("status")); + contractMeta.setOwner(document.get("owner")); + contractMeta.setPubkey(document.get("pubkey")); + contractMeta.setReadmeStr(document.get("readmeStr")); + contractMeta.setNodeAddr(document.get("nodeAddr")); + contractMeta.setPngUrl(document.get("pngUrl")); + contractMeta.setName(document.get("name")); + contractMeta.setDoi(document.get("doi")); + contractMeta.setBuildTime(Long.parseLong(document.get("buildTime"))); + contractMeta.setDoip("DOIP://86.470.5000/" + document.get("name")); + contractMetaList.add(contractMeta); + } + } + resultModel.setContractMetaList(contractMetaList); + resultModel.setCurPage(page); + Long pageCount = + docs.totalHits.value % PAGE_SIZE > 0 + ? (docs.totalHits.value) / PAGE_SIZE + 1 + : (docs.totalHits.value) / PAGE_SIZE; + resultModel.setPageCount(pageCount); + return resultModel; + } + + @Action(async = true, userPermission = 1L) + public synchronized void onRequestReadMe(JsonObject json, ResultCallback rc) + throws IOException { + // try { + // Thread.sleep(2000); + // } catch (InterruptedException e) { + // e.printStackTrace(); + // } + CMNode node = NodeCenterActions.nodeInfos.get(controller.pubKey); + String nodeAddr = getNodeAddr(node.masterAddress); + String name = json.get("name").getAsString(); + // /DOIP/Hello1/assets/logo.png + // String nodeAddr="127.0.0.1:21030"; + String pngUrl = "http://" + nodeAddr + "/DOIP/" + name + "/assets/logo.png"; + System.out.println( + "node.masterAddress: " + + node.masterAddress + + "==========" + + "nodeAddr: " + + nodeAddr); + System.out.println("name: " + name + "pngUrl: " + pngUrl); + String contractID = json.get("contractID").getAsString(); + String status = json.get("status").getAsString(); + String owner = json.get("owner").getAsString(); + String pubkey = json.get("pubkey").getAsString(); + String readmeStr = json.get("readmeStr").getAsString(); + String doi = json.get("doi").getAsString(); + String buildTime = String.valueOf(json.get("buildTime").getAsLong()); + Document document = new Document(); + document.add(new StringField("contractID", contractID, Field.Store.YES)); + document.add(new StringField("status", status, Field.Store.YES)); + document.add(new StringField("owner", owner, Field.Store.YES)); + document.add(new StringField("pubkey", pubkey, Field.Store.YES)); + document.add(new TextField("readmeStr", readmeStr, Field.Store.YES)); + document.add(new StringField("nodeAddr", nodeAddr, Field.Store.YES)); + document.add(new StringField("pngUrl", pngUrl, Field.Store.YES)); + document.add(new StringField("name", name, Field.Store.YES)); + document.add(new StringField("doi", doi, Field.Store.YES)); + document.add(new StringField("buildTime", buildTime, Field.Store.YES)); + LOGGER.info("Index Meta:" + contractID + " str:" + readmeStr); + LOGGER.info("name:" + name + "=>" + "doi:" + doi + "=>" + "buildTime:" + buildTime); + indexWriter.addDocument(document); + indexWriter.commit(); + isEmpty = false; + // TODO @fanbo 更新lucene + } + + private String getNodeAddr(String masterAddress) { + String[] temp = masterAddress.split(":"); + temp[1] = String.valueOf(Integer.parseInt(temp[1]) - 1); + String nodeAddr = temp[0] + ":" + temp[1]; + return nodeAddr; + } + + @Action(async = true, userPermission = 1L) + public void getMetabyReadme(JsonObject json, ResultCallback rc) { + String result = getMetabyReadme(json); + JsonObject object = new JsonObject(); + if (json.has("requestID")) object.add("responseID", json.get("requestID")); + System.out.println("zzzResult" + result); + object.add("result", JsonParser.parseString(result)); + object.addProperty("action", "getMetabyReadme"); + rc.onResult(object.toString()); + } + + @Action(async = true, userPermission = 1L) + public void segmentWord(JsonObject json, ResultCallback rc) throws IOException { + //Analyzer analyzer = new IKAnalyzer(true); + CharArraySet stopWords = CharArraySet.unmodifiableSet(WordlistLoader.getWordSet(new InputStreamReader(Objects.requireNonNull(MetaIndexAction.class.getClassLoader().getResourceAsStream( + "org/bdware/server/stopwords.txt")), StandardCharsets.UTF_8))); + Analyzer analyzer = new SmartChineseAnalyzer(stopWords); + JsonObject object = new JsonObject(); + + String words = "我喜欢区块链,我想试用一下这个,我是做大数据处理的,我是科技局的管理员"; // 从json拿 + TokenStream stream = null; + if (json.has("requestID")) object.add("responseID", json.get("requestID")); + try { + stream = analyzer.tokenStream("myfield", words); + CharTermAttribute charTermAtt = stream.addAttribute(CharTermAttribute.class); + stream.reset(); + int count = 0; + while (stream.incrementToken()) { + object.addProperty("" + count++, charTermAtt.toString()); + System.out.println(charTermAtt.toString()); + } + stream.end(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + rc.onResult(object.toString()); + try { + stream.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + @Action(async = true, userPermission = 1L) + public void getMetabyCID(JsonObject json, ResultCallback rc) { + String result = getMetabyCID(json); + JsonObject object = new JsonObject(); + if (json.has("requestID")) object.add("responseID", json.get("requestID")); + object.add("result", JsonParser.parseString(result)); + object.addProperty("action", "getMetabyCID"); + rc.onResult(object.toString()); + } + + @Action(async = true, userPermission = 1L) + public void getMetabyOwner(JsonObject json, ResultCallback rc) { + String result = getMetabyOwner(json); + JsonObject object = new JsonObject(); + if (json.has("requestID")) object.add("responseID", json.get("requestID")); + object.add("result", JsonParser.parseString(result)); + object.addProperty("action", "getMetabyOwner"); + rc.onResult(object.toString()); + } + + @Action(async = true, userPermission = 1L) + public void getMetabyPubkey(JsonObject json, ResultCallback rc) { + String result = getMetabyPubkey(json); + JsonObject object = new JsonObject(); + if (json.has("requestID")) object.add("responseID", json.get("requestID")); + object.add("result", JsonParser.parseString(result)); + object.addProperty("action", "getMetabyPubkey"); + rc.onResult(object.toString()); + } + + @Action(async = true, userPermission = 1L) + public void delIndexbyCID(JsonObject json, ResultCallback rc) { + delIndexbyCID(json); + } + + @Action(async = true, userPermission = 1L) + public void delIndexbyOwner(JsonObject json, ResultCallback rc) { + delIndexbyOwner(json); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/MultiPointContractInfo.java b/src/main/java/org/bdware/server/nodecenter/MultiPointContractInfo.java new file mode 100644 index 0000000..b26a440 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/MultiPointContractInfo.java @@ -0,0 +1,19 @@ +package org.bdware.server.nodecenter; + +import org.bdware.sc.bean.ContractExecType; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class MultiPointContractInfo { + public List members; //pubKey + public ContractExecType type; + AtomicInteger ai = new AtomicInteger(); + + public transient ContractExecutor rcf; + + public String getNextSeq(){ + System.out.println("MultiPointContractInfo获得下一个序号" + (ai.get() + 1)); + return ai.getAndIncrement() + ""; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCClientActions.java b/src/main/java/org/bdware/server/nodecenter/NCClientActions.java new file mode 100644 index 0000000..115fa96 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCClientActions.java @@ -0,0 +1,86 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.bean.OtherNCInfo; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.Action; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.zz.gmhelper.SM2Util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class NCClientActions { + private static final Logger LOGGER = LogManager.getLogger(NCClientActions.class); + private final NCClientHandler handler; + + public NCClientActions(NCClientHandler h) { + handler = h; + } + + @Action + public void onSessionID(JsonObject json, ResultCallback cb) { + LOGGER.info("[NCClientActions] onSessionID : "); + + Map map = new HashMap<>(); + LOGGER.debug("[onSessionID]:" + json.toString()); + map.put("action", "getNCConnectPermission"); + map.put("id", OtherNCProxy.instance.sm2.getPublicKeyStr()); + byte[] signature = "no signature".getBytes(); + try { + signature = SM2Util.sign(OtherNCProxy.instance.sm2.getPrivateKeyParameter(), (OtherNCProxy.instance.sm2.getPublicKeyStr() + json.get("session").getAsString()) + .getBytes()); + } catch (CryptoException e) { + e.printStackTrace(); + } + map.put("signature", ByteUtils.toHexString(signature)); + sendMsg(JsonUtil.toJson(map)); + } + + @Action + public void onGetNCConnectPermission(JsonObject json, ResultCallback resultCallback) { + LOGGER.info("[NCClientActions] onGetNCConnectPermission : "); + + if (json.get("data").getAsString().equals("success")) { + handler.hasPermission = true; + LOGGER.info("[NCClientActions] onGetNCConnectPermission : permission true"); + } + } + + @Action + public void receiveUpdate(JsonObject json, ResultCallback resultCallback) { + LOGGER.info("[NCClientActions] receiveUpdate : "); + List info = new ArrayList(); + + if (json.has("empty")) { + LOGGER.info("receive from NC ,do not have any cp."); + } else { + info = JsonUtil.fromJson(json.get("contracts").getAsString(), new TypeToken>() { + }.getType()); + } + + LOGGER.info(JsonUtil.toJson(info)); + + String id = json.get("id").getAsString(); + OtherNCProxy.instance.updateOtherNCInfo(id, info); + + String add = json.get("address").getAsString(); + LOGGER.info("[NCClientActinos] close connect " + add); + OtherNCProxy.instance.connList.get(add).close(); + } + + public void sendMsg(String str) { + try { + handler.sendMsg(str); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCClientHandler.java b/src/main/java/org/bdware/server/nodecenter/NCClientHandler.java new file mode 100644 index 0000000..0797fab --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCClientHandler.java @@ -0,0 +1,114 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.ActionExecutor; + +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NCClientHandler extends SimpleChannelInboundHandler { + private static final Logger LOGGER = LogManager.getLogger(NCClientHandler.class); + static ExecutorService executorService = Executors.newFixedThreadPool(5); + public boolean hasPermission; //是否在目标NC上有权限 + public NCClientActions actions; + Channel channel; + ActionExecutor ae; + ChannelHandlerContext ctx; + private boolean isConnected; + + public NCClientHandler() { + actions = new NCClientActions(this); + ae = new ActionExecutor<>(executorService, actions); + isConnected = false; + hasPermission = false; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + channel = ctx.channel(); + isConnected = true; + this.ctx = ctx; + Map getSession = new HashMap<>(); + getSession.put("action", "getSessionID"); + sendMsg(JsonUtil.toJson(getSession)); + LOGGER.debug("[NCClientHandler] getSessionID:" + JsonUtil.toJson(getSession)); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + isConnected = false; + hasPermission = false; + try { + channel.close(); + channel = null; + } catch (Exception e) { + } + } + + public synchronized void sendMsg(String msg) { + channel.writeAndFlush(Unpooled.wrappedBuffer(msg.getBytes())); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf bb = (ByteBuf) msg; + try { + final JsonObject arg = + new JsonParser() + .parse(new InputStreamReader(new ByteBufInputStream(bb))) + .getAsJsonObject(); + if (arg.has("action")) { + final String action = arg.get("action").getAsString(); + ae.handle( + action, + arg, + new ResultCallback() { + @Override + public void onResult(String str) { + sendMsg(str); + } + }); + } + + } catch (java.lang.IllegalArgumentException e) { + LOGGER.debug("[NCClientHandler] can't handle action:" + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + System.err.println("[NCClientHandler] catchException"); + cause.printStackTrace(); + } + + public void close() { + try { + isConnected = false; + if (channel != null) channel.close(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + channel = null; + } + } + + public boolean isOpen() { + return ctx != null && ctx.channel().isOpen(); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCElectMasterUtil.java b/src/main/java/org/bdware/server/nodecenter/NCElectMasterUtil.java new file mode 100644 index 0000000..aca46e1 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCElectMasterUtil.java @@ -0,0 +1,210 @@ +package org.bdware.server.nodecenter; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; + +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class NCElectMasterUtil { + public static final Map electInfos = new ConcurrentHashMap<>(); //key is contractID + private static final Logger LOGGER = LogManager.getLogger(NCElectMasterUtil.class); + + public static class ElectInfo { + final long delay = 5000; + final AtomicBoolean startElect = new AtomicBoolean(false); + String formerMaster; + String uniNumber; + long lastTime = System.currentTimeMillis(); + int onlineNum; //除旧的master之外的在线节点数 + String contractID; + String mems; //执行这个合约的所有节点的pubKey + private Map nodeID2LastExe = new ConcurrentHashMap<>(); //key is nodeID + private Timer timer; + + + public ElectInfo(String m, String con, String members, String uni) { + formerMaster = m; + contractID = con; + mems = members; + uniNumber = uni; + String[] mem = members.split(","); + +/* try { + Thread.sleep(2000); //让NC发现崩溃节点 + } catch (InterruptedException e) { + e.printStackTrace(); + }*/ + + for (String memID : mem) { + if (memID == null || memID.length() == 0) + continue; + + if (NodeCenterActions.nodeInfos.containsKey(memID) && !memID.equals(formerMaster)) { + onlineNum++; + } + } + NodeCenterServer.scheduledThreadPool.scheduleWithFixedDelay( + () -> { + // cancel the election if no nodes find the master's crash in delay + 2 seconds + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd.HH:mm:ss.SSS"); + if (System.currentTimeMillis() - lastTime > (delay + 15000)) { + LOGGER.info("lastTime=" + df.format(lastTime) + " cancel the election"); + cancel(); + } + // start timeout election +// if (electInfos.containsKey(contractID) && nodeID2LastExe.size() == onlineNum) { +// elect(); +// } + }, + delay + 1500, + delay + 1500, + TimeUnit.MILLISECONDS); + //timer.schedule(task, dealy + 2000); + + LOGGER.info("new election of contract " + contractID + " is added to electInfos, " + + onlineNum + " node is online"); + } + + public void cancel() { + if (timer != null) { + timer.cancel(); + timer = null; + } + if (electInfos.containsKey(contractID)) { + electInfos.remove(contractID); + } + } + + public synchronized void put(String nodeID, int lastExe, String master, String mem2, String uniNum) { + LOGGER.info("put nodeID=" + nodeID); + + //确保该合约某时只能有一个选举,其他的选举请求被忽略 + if (!master.equals(formerMaster)) { + LOGGER.debug("[NCElectMasterUtil] master error!"); + return; + } + if (nodeID.equals(formerMaster)) { + LOGGER.debug("former master voted,error!"); + return; + } + if (master.equals("null")) { + LOGGER.info("uniNum=" + uniNum + " uniNumber=" + uniNumber); + if (!uniNum.equals(uniNumber)) { + LOGGER.debug("already has re-elect process when master is null"); + return; + } + } + + long now = System.currentTimeMillis(); + //认为是一个新的选举,之前的作废 + if (now - lastTime > delay) { + LOGGER.info("[NCElectMasterUtil] time error!"); + cancel(); + synchronized (electInfos) { + //electInfos.remove(contractID); + NCElectMasterUtil.electInfos.put(contractID, new ElectInfo(master, contractID, mem2, uniNum)); + ElectInfo eleInfo = electInfos.get(contractID); + eleInfo.put(nodeID, lastExe, master, mem2, uniNum); + } + return; + } + lastTime = now; + nodeID2LastExe.put(nodeID, lastExe); + + LOGGER.info("[ElectInfo] 加入合约 " + contractID + " 的选举信息节点" + nodeID.substring(0, 5)); + + if (nodeID2LastExe.size() == onlineNum) { + cancel(); + elect(); + } + } + + public synchronized void elect() { + LOGGER.info("[ElectInfo] 开始选举"); + + ElectMasterTimeRecorder.startElect = System.currentTimeMillis(); + + synchronized (startElect) { + startElect.set(true); + } + + //更新路由信息,这个合约的master暂时为null + if (NodeCenterActions.nodeInfos.containsKey(formerMaster)) { + CMNode node = NodeCenterActions.nodeInfos.get(formerMaster); + if (node != null) { + synchronized (node) { + for (ContractDesp cd : node.contracts) { + if (cd.contractID.equals(contractID) || cd.contractName.equals(contractID)) { + cd.setIsMaster(false); + LOGGER.debug("设置节点 " + node.pubKey.substring(0, 5) + " 的合约 " + contractID + " isMaster=" + false); + break; + } + } + } + } + } + + int maxExeSeq = Integer.MIN_VALUE; + String newMaster = ""; + for (String id : nodeID2LastExe.keySet()) { + if (nodeID2LastExe.get(id) > maxExeSeq) { + newMaster = id; + } + } + + LOGGER.info("[NCElectMasterUtil] 选举出新的master " + newMaster); + + cancel(); + //electInfos.remove(contractID); + + //通知新的master让它发起重连操作,并在所有节点重连成功之后开启master的恢复 + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + LOGGER.info("开始计算需要连接新master的都有哪些节点:"); + StringBuilder onlineMems = new StringBuilder(); //不包含旧的master + for (String memID : nodeID2LastExe.keySet()) { + if (memID == null || memID.length() == 0) + continue; + + LOGGER.info("[NCElectMasterUtil] 查看节点 " + memID.substring(0, 5) + " 是否还在线"); + if (NodeCenterActions.nodeInfos.containsKey(memID)) { + if (memID.equals(formerMaster)) + continue; + + LOGGER.info("onlineMems中加入 " + memID.substring(0, 5)); + if (onlineMems.toString().equals("")) { + onlineMems = new StringBuilder(memID); + } else { + onlineMems.append(","); + onlineMems.append(memID); + } + } + } + + ElectMasterTimeRecorder.findNewMaster = System.currentTimeMillis(); + ElectMasterTimeRecorder.newMaster = newMaster; + + LOGGER.info("通知新的master让它发起重连操作 " + onlineMems); + CMNode masterNode = NodeCenterActions.nodeInfos.get(newMaster); + Map req = new HashMap<>(); + req.put("action", "newMasterStart"); + req.put("members", mems); + req.put("onlineMems", onlineMems.toString()); + req.put("contractID", contractID); + req.put("formerMaster", formerMaster); + masterNode.connection.controller.sendMsg(JsonUtil.toJson(req)); + } + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCHttpHandler.java b/src/main/java/org/bdware/server/nodecenter/NCHttpHandler.java new file mode 100644 index 0000000..b552ab1 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCHttpHandler.java @@ -0,0 +1,330 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.util.ExceptionUtil; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.bdware.server.action.Action; +import org.bdware.server.action.ActionExecutor; +import org.bdware.server.action.HttpResultCallback; +import org.bdware.server.http.HttpFileHandleAdapter; +import org.bdware.server.http.URIHandler; +import org.bdware.server.http.URIPath; +import org.bdware.server.permission.Role; +import org.zz.gmhelper.SM2Util; + +import java.io.*; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.netty.handler.codec.http.HttpResponseStatus.OK; + +@Sharable +public class NCHttpHandler extends SimpleChannelInboundHandler { + private static final String PARAM_ACTION = "action"; + private static final String UNSUPPORTED_HTTP_METHOD = "{\"msg\":\"unsupported http method\"}"; + private static final String UNSUPPORTED_ACTION = "{\"msg\":\"unsupported action\"}"; + private static final Logger LOGGER = LogManager.getLogger(NCHttpHandler.class); + private static final ActionExecutor actionExecutor = + new ActionExecutor( + NodeCenterFrameHandler.executorService, new NodeCenterActions(null)) { + + @Override + public boolean checkPermission(Action a, JsonObject arg, long per) { + boolean flag = a.httpAccess(); + long val = a.userPermission(); // 使用者必须达到的permission + long permission = arg.get("permission").getAsLong(); + LOGGER.debug("permission" + permission); + LOGGER.debug("htttttttttttttp--userpermission:" + val); + LOGGER.debug("htttttttttttttp--permission:" + per); // 现有的permission + boolean flag2 = false; // 启动http权限后应改为false + String status = "refuse"; + String action = arg.get("action").getAsString(); + + if (val == 0) { + flag2 = true; + } else { + flag2 = (permission & val) == val; + } + + if (flag && flag2) { + status = "accept"; + } + + String pubkey = "anonymity"; + if (arg.has("pubKey")) { + pubkey = arg.get("pubKey").getAsString(); + } + NodeCenterServer.nodeHttpLogDB.put( + action, + "{\"action\":\"" + + action + + "\",\"pubKey\":\"" + + pubkey + + "\",\"date\":" + + System.currentTimeMillis() + + "}"); + LOGGER.debug("[NCHttpHandler] flag = " + flag + " flag2 = " + flag2); + return flag && flag2; + } + }; + static TypeToken> token = new TypeToken>() {}; + private final HttpFileHandleAdapter fileAdapter; + // public static ExecutorService executor = Executors.newFixedThreadPool(5); + public AtomicInteger counter = new AtomicInteger(0); + private URIHandler handler; + + public NCHttpHandler() { + FileFilter fileFilter = pathname -> !pathname.getAbsolutePath().contains(".."); + fileAdapter = + new HttpFileHandleAdapter(new File("./WebContent/").getAbsolutePath(), fileFilter) { + @URIPath("/") + public void loadFile(ChannelHandlerContext ctx, FullHttpRequest req) + throws Exception { + super.channelRead0(ctx, req); + } + }; + handler = new URIHandler(); + handler.register(this); + handler.register(fileAdapter); + handler.printURIHandlers(); + } + + public static ActionExecutor getActionExecutor() { + return actionExecutor; + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) { + ctx.flush(); + } + + @URIPath({"/NodeCenter", "/SCIDE/SCExecutor", "/SCIDE/CMManager", "/SCIDE/SCManager"}) + public void parseGetAndHandle(ChannelHandlerContext ctx, FullHttpRequest req) { + QueryStringDecoder decoderQuery = new QueryStringDecoder(req.uri()); + Map> params = decoderQuery.parameters(); + JsonObject map = new JsonObject(); + for (String key : params.keySet()) { + List val = params.get(key); + if (val != null) map.addProperty(key, val.get(0)); + } + String uri = URLDecoder.decode(req.uri()).split("\\?")[1]; + int index = uri.lastIndexOf('&'); + String str = uri; + if (index != -1) { + str = uri.substring(0, index); + } + injectPermission(map, str); + handleReq(map, ctx); + } + + @URIPath( + method = org.bdware.server.http.HttpMethod.POST, + value = {"/NodeCenter", "/SCIDE/SCExecutor", "/SCIDE/CMManager", "/SCIDE/SCManager"}) + public void parsePostAndHandle(ChannelHandlerContext ctx, FullHttpRequest req) + throws UnsupportedEncodingException { + ByteBuf content = req.content(); + JsonObject map = + JsonParser.parseReader(new InputStreamReader(new ByteBufInputStream(content))) + .getAsJsonObject(); + // FIXME 感觉不太对劲 + String uri = URLDecoder.decode(req.uri(), "utf-8").split("\\?")[1]; + int index = uri.lastIndexOf('&'); + String str = uri; + if (index != -1) { + str = uri.substring(0, index); + } + injectPermission(map, str); + handleReq(map, ctx); + } + + private JsonObject parseArgInQuery(FullHttpRequest request) { + QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri()); + Map> parameters = decoderQuery.parameters(); + JsonObject transformedParam = new JsonObject(); + for (String key : parameters.keySet()) { + List val = parameters.get(key); + if (val != null) transformedParam.addProperty(key, val.get(0)); + } + return transformedParam; + } + + @URIPath( + method = org.bdware.server.http.HttpMethod.GET, + value = {"/doip"}) + public void handleDOIP(ChannelHandlerContext ctx, FullHttpRequest req) { + ByteBuf content = req.content(); + JsonObject map = parseArgInQuery(req); + // FIXME 感觉不太对劲 + String uri = URLDecoder.decode(req.uri()).split("\\?")[1]; + int index = uri.lastIndexOf('&'); + String str = uri; + if (index != -1) { + str = uri.substring(0, index); + } + injectPermission(map, str); + + DefaultFullHttpResponse response = + new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + OK, + Unpooled.wrappedBuffer(MetaIndexAction.search(map).getBytes())); + ChannelFuture f = ctx.write(response); + f.addListener(ChannelFutureListener.CLOSE); + + // TODO @fanbo Http查询接口,需支持各类查询,按owner/按关键词/.... + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { + // logger.debug("[NCHttpHandler] TID:" + Thread.currentThread().getId() + + // msg.toString()); + if (msg instanceof FullHttpRequest) { + FullHttpRequest request = (FullHttpRequest) msg; + handler.handle(ctx, request); + } + } + + public String getRole(String pubKey) { + try { + String ret = + KeyValueDBUtil.instance.getValue( + NCTables.ConfigDB.toString(), "__CenterManager__"); + if (ret != null && ret.length() > 0) { + boolean isCenterManager = (ret.equals(pubKey)); // 表示此节点是否是平台管理员 + ret = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubKey); + String role = ""; + if (isCenterManager) { + role = "CenterManager,"; + } + if (null != ret && !ret.isEmpty()) { + role += ret; + } + if (role.endsWith(",")) { + role = role.substring(0, role.length() - 1); + } else if (role.isEmpty()) { + role = Role.Anonymous.name(); + } + return role; + } + return Role.Anonymous.name(); + } catch (Exception e) { + e.printStackTrace(); + return Role.Anonymous.name(); + } + } + + private void injectPermission(JsonObject map, String str) { + if (map.has("pubKey")) { + String pubkey = map.get("pubKey").getAsString(); + boolean verify = SM2Util.plainStrVerify(pubkey, str, map.get("sign").getAsString()); + LOGGER.debug("[CMHttpHandler] HTTP POST 请求验签为 " + verify); + + if (verify) { + // 查permission + String ret = getRole(pubkey); + long permission; + if (ret != null && ret.length() > 0) { + permission = 0x86000d41L | Role.compoundValue(ret.split(",")); + } else { + permission = Role.compoundValue(ret.split(",")); + } + map.addProperty("permission", permission + ""); + LOGGER.debug("[CMHttpHandler] http 请求查看用户权限为 : " + permission); + } else { + map.addProperty("permission", 0 + ""); + } + } else { + map.addProperty("permission", 0 + ""); + } + } + + private void handleReq(final JsonObject map, final ChannelHandlerContext ctx) { + try { + map.addProperty("fromHttp", "true"); + String ret = null; + String action = null; + if (!map.has("action")) { + ret = UNSUPPORTED_ACTION; + } else { + action = map.get("action").getAsString(); + } + if (action != null) { + HttpResultCallback cb; + + if (action.equals("takeScreenshot")) { + cb = new HttpResultCallback(ctx, map.get("callback").getAsString()); + cb.setDecodeBase64(); + cb.addHeader("Content-type", "image/png"); + + } else if (action.equals("executeContract")) { + cb = new HttpResultCallback(ctx, map.get("callback").getAsString()); + cb.addHeader("charset", "utf-8"); + cb.addHeader("Content-type", "text/plain"); + + } else { + + if (map.has("callback")) + cb = new HttpResultCallback(ctx, map.get("callback").getAsString()); + else cb = new HttpResultCallback(ctx, null); + + cb.addHeader("charset", "utf-8"); + cb.addHeader("Content-type", "application/json"); + } + actionExecutor.handle(action, map, cb); + } else { + DefaultFullHttpResponse response = + new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, OK, Unpooled.wrappedBuffer(ret.getBytes())); + ChannelFuture f = ctx.write(response); + f.addListener(ChannelFutureListener.CLOSE); + } + } catch (IllegalArgumentException e) { + DefaultFullHttpResponse response = + new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + OK, + Unpooled.wrappedBuffer(e.getMessage().getBytes())); + ChannelFuture f = ctx.write(response); + f.addListener(ChannelFutureListener.CLOSE); + + } catch (Exception e) { + Map ret = new HashMap<>(); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + ret.put("msg", bo.toString()); + DefaultFullHttpResponse response = + new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + OK, + Unpooled.wrappedBuffer(JsonUtil.toJson(ret).getBytes())); + ChannelFuture f = ctx.write(response); + f.addListener(ChannelFutureListener.CLOSE); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + LOGGER.warn("catch exception in " + this + ": " + cause.getClass().getSimpleName()); + LOGGER.debug(ExceptionUtil.exceptionToString(cause)); + ctx.close(); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCManagerAction.java b/src/main/java/org/bdware/server/nodecenter/NCManagerAction.java new file mode 100644 index 0000000..b6d5696 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCManagerAction.java @@ -0,0 +1,473 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.netty.util.internal.StringUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.Jedion.KV; +import org.bdware.sc.conn.ByteUtil; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.encrypt.HardwareInfo; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.Action; +import org.bdware.server.permission.Role; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.zz.gmhelper.BCECUtil; +import org.zz.gmhelper.SM2Util; + +import java.text.SimpleDateFormat; +import java.util.*; + +// CenterPortal用户管理相关 +public class NCManagerAction { + // key为centerManager的数据表示平台管理员(中心管理员) + public static final String centerManger = "__CenterManager__"; + static final String Licence = "__LICENCE___"; + static final ECPublicKeyParameters licencePub = + BCECUtil.createECPublicKeyFromStrParameters( + "04844412ecb77b07d5ad7097c488ae9dff1013b310d2311f8bd84c491642011e1a6a7041bae8c2ad75da29c27e320bd430abc723cf6bcb0490afc82cc26e6d5637", + SM2Util.CURVE, + SM2Util.DOMAIN_PARAMS); + private static final Logger LOGGER = LogManager.getLogger(NCManagerAction.class); + String sessionID = null; + String pubKey; + Random random = new Random(); + private NodeCenterWSFrameHandler handler; + + public NCManagerAction(NodeCenterWSFrameHandler nodeCenterWSFrameHandler) { + this.handler = nodeCenterWSFrameHandler; + pubKey = null; + } + + public static boolean isCenterManager(String pubkey) { + String ret = KeyValueDBUtil.instance.getValue(NCTables.ConfigDB.toString(), centerManger); + LOGGER.debug("centerManger: " + ret); + return !StringUtil.isNullOrEmpty(ret) + && !StringUtil.isNullOrEmpty(pubkey) + && pubkey.equals(ret); + } + + @Action(userPermission = 0) + public void getNodeSessionID(JsonObject json, ResultCallback resultCallback) { + getSessionID(json, resultCallback); + } + + @Action(userPermission = 0) + public void getSessionID(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + sessionID = random.nextLong() + "_session"; + Map result = new HashMap<>(); + result.put("action", "onSessionID"); + result.put("session", sessionID); + resultCallback.onResult(JsonUtil.toJson(result)); + long end = System.currentTimeMillis(); + LOGGER.debug("time:" + (end - start) + "data:" + JsonUtil.toJson(result)); + } + + @Action(userPermission = 0) + public void getNodeRole(JsonObject json, ResultCallback resultCallback) { + getRole(json, resultCallback); + } + + @Action(userPermission = 0) + public void getManagerPubkey(JsonObject json, ResultCallback resultCallback) { + String ret = KeyValueDBUtil.instance.getValue(NCTables.ConfigDB.toString(), centerManger); + resultCallback.onResult("{\"action\":\"onGetManagerPubkey\",\"data\":\"" + ret + "\"}"); + } + + public void getRole(JsonObject json, ResultCallback resultCallback) { + if (pubKey == null) { + resultCallback.onResult( + "{\"action\":\"onLogin\",\"data\":\"" + Role.Anonymous.name() + "\"}"); + return; + } + try { + String ret = + KeyValueDBUtil.instance.getValue(NCTables.ConfigDB.toString(), centerManger); + if (ret != null && ret.length() > 0) { + boolean isCenterManager = (ret.equals(pubKey)); // 表示此节点是否是平台管理员 + ret = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubKey); + String role = ""; + if (isCenterManager) { + role = "CenterManager,"; + } + if (null != ret && !ret.isEmpty()) { + role += ret; + } + if (role.endsWith(",")) { + role = role.substring(0, role.length() - 1); + } else if (role.isEmpty()) { + role = Role.Anonymous.name(); + } + handler.setPermission(Role.compoundValue(role.split(","))); + resultCallback.onResult("{\"action\":\"onLogin\",\"data\":\"" + role + "\"}"); + } else { + KeyValueDBUtil.instance.setValue( + NCTables.ConfigDB.toString(), centerManger, pubKey); + handler.setPermission(0x30000ffL); + resultCallback.onResult("{\"action\":\"onLogin\",\"data\":\"CenterManager\"}"); + } + } catch (Exception e) { + e.printStackTrace(); + resultCallback.onResult( + "{\"action\":\"onLogin\",\"data\":\"" + Role.Anonymous.name() + "\"}"); + } + } + + @Action(userPermission = 0) + public void login(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + if (sessionID == null) { + resultCallback.onResult( + "{\"action\":\"onLogin\",\"data\":\"" + Role.Anonymous.name() + "\"}"); + return; + } + String signature = json.get("signature").getAsString(); + String pubKey = json.get("pubKey").getAsString(); + + boolean result = SM2Util.plainStrVerify(pubKey, sessionID, signature); + LOGGER.debug("session:" + (sessionID)); + if (result) { + this.pubKey = pubKey; + LOGGER.debug("设置公钥" + pubKey); + getRole(json, resultCallback); + } else { + resultCallback.onResult( + "{\"action\":\"onLogin\",\"data\":\"" + Role.Anonymous.name() + "\"}"); + } + long end = System.currentTimeMillis(); + LOGGER.debug("time:" + (end - start)); + } + + @Action(userPermission = 0) + public void applyRole(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + if (pubKey == null) { + resultCallback.onResult("{\"action\":\"onApplyRole\",\"data\":\"missing pubKey\"}"); + return; + } + if (json.has("role")) { + Role role = Role.parse(json.get("role").getAsString()); + if (role != Role.Anonymous) { + applyAtDB(role, resultCallback, start); + return; + } + } + resultCallback.onResult("{\"action\":\"onApplyRole\",\"data\":\"failed\"}"); + } + + @Action(userPermission = 1 << 6) + public void addNode(JsonObject json, ResultCallback resultCallback) { + try { + KeyValueDBUtil.instance.setValue( + NCTables.NodeUser.toString(), + json.get("nodePubKey").getAsString(), + Role.Node.toString()); + KeyValueDBUtil.instance.setValue( + NCTables.NodeTime.toString(), + json.get("nodePubKey").getAsString(), + Long.toString(new Date().getTime())); + resultCallback.onResult("{\"action\":\"onAddNodes\",\"data\":\"success\"}"); + } catch (Exception e) { + e.printStackTrace(); + resultCallback.onResult("{\"action\":\"onAddNodes\",\"data\":\"failed\"}"); + } + } + + private void applyAtDB(Role role, ResultCallback resultCallback, long start) { + String str = KeyValueDBUtil.instance.getValue(NCTables.ApplyRole.toString(), pubKey); + String already = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubKey); + if (already != null && already.contains(role.toString())) { + resultCallback.onResult("{\"action\":\"onApplyRole\",\"data\":\"already has!\"}"); + return; + } + if (str == null || str.length() == 0) { + KeyValueDBUtil.instance.setValue(NCTables.ApplyRole.toString(), pubKey, role.name()); + KeyValueDBUtil.instance.setValue( + NCTables.ApplyTime.toString(), pubKey, Long.toString(new Date().getTime())); + } else { + if (!str.contains(role.name())) { + KeyValueDBUtil.instance.setValue( + NCTables.ApplyRole.toString(), pubKey, str + "," + role.name()); + KeyValueDBUtil.instance.setValue( + NCTables.ApplyTime.toString(), pubKey, Long.toString(new Date().getTime())); + } + } + resultCallback.onResult("{\"action\":\"onApplyRole\",\"data\":\"success\"}"); + } + + @Action(userPermission = 1 << 3) + public void listAllUsers(JsonObject json, ResultCallback resultCallback) { + List kv = KeyValueDBUtil.instance.getKeyValues(NCTables.NodeUser.toString()); + List time = KeyValueDBUtil.instance.getKeyValues(NCTables.NodeTime.toString()); + Map ret = new HashMap<>(); + ResultBack result = new ResultBack(); + ret.put("kv", kv); + ret.put("time", time); + result.action = "onListAllUsers"; + result.data = ret; + resultCallback.onResult(JsonUtil.toJson(result)); + } + + @Action(userPermission = 1 << 2) + public void authNodeManager(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + String pubKey = json.get("pubKey").getAsString(); + boolean isAccept = json.get("isAccept").getAsBoolean(); + LOGGER.debug("[NCManagerAction] " + json.toString()); + if (pubKey == null || pubKey.length() == 0) { + resultCallback.onResult( + "{\"action\":\"onAuthNodeManager\",\"data\":\"missing pubKey\"}"); + return; + } + if (!isAccept) { + KeyValueDBUtil.instance.delete(NCTables.ApplyRole.toString(), pubKey); + KeyValueDBUtil.instance.delete(NCTables.ApplyTime.toString(), pubKey); + resultCallback.onResult("{\"action\":\"onAuthNodeManager\",\"data\":\"success\"}"); + return; + } + String already = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubKey); + String roles = KeyValueDBUtil.instance.getValue(NCTables.ApplyRole.toString(), pubKey); + if (roles == null || roles.length() == 0) { + resultCallback.onResult("{\"action\":\"onAuthNodeManager\",\"data\":\"empty apply\"}"); + return; + } + if (already != null && already.length() > 0) { + already += "," + roles; + } else { + already = roles; + } + + KeyValueDBUtil.instance.setValue(NCTables.NodeUser.toString(), pubKey, already); + KeyValueDBUtil.instance.setValue( + NCTables.NodeTime.toString(), pubKey, Long.toString(new Date().getTime())); + KeyValueDBUtil.instance.delete(NCTables.ApplyRole.toString(), pubKey); + KeyValueDBUtil.instance.delete(NCTables.ApplyTime.toString(), pubKey); + resultCallback.onResult("{\"action\":\"onAuthNodeManager\",\"data\":\"success\"}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[authNodeManager:time]" + (end - start)); + } + + @Action(userPermission = 1 << 4) + public void listApplyList(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + List kv = KeyValueDBUtil.instance.getKeyValues(NCTables.ApplyRole.toString()); + List time = KeyValueDBUtil.instance.getKeyValues(NCTables.ApplyTime.toString()); + Map ret = new HashMap<>(); + ret.put("kv", kv); + ret.put("time", time); + ResultBack result = new ResultBack(); + result.action = "onListApplyList"; + result.data = ret; + resultCallback.onResult(JsonUtil.toJson(result)); + long end = System.currentTimeMillis(); + LOGGER.debug("[listApplyList:time]" + (end - start)); + } + + @Action(userPermission = 1 << 5) + public void delete(JsonObject json, ResultCallback resultCallback) { + String pubKey = json.get("pubKey").getAsString(); + KeyValueDBUtil.instance.delete(NCTables.NodeUser.toString(), pubKey); + KeyValueDBUtil.instance.delete(NCTables.NodeTime.toString(), pubKey); + resultCallback.onResult("{\"action\":\"onDelete\",\"data\":\"success\"}"); + } + + @Action(userPermission = 1 << 4) + public void listLicence(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + String licence = KeyValueDBUtil.instance.getValue(NCTables.ConfigDB.toString(), Licence); + if (licence == null || licence.length() == 0) { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":" + 0 + ",\"dueDate\":\"已过期\"}"); + return; + } + json = JsonParser.parseString(licence).getAsJsonObject(); + String sign = json.get("sign").getAsString(); + String content = json.get("content").getAsString(); + if (StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(content)) { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":0,\"dueDate\":\"已过期\"}"); + return; + } + long expiredDate; + long nodeCount; + try { + JsonObject jo = JsonParser.parseString(content).getAsJsonObject(); + nodeCount = jo.get("nodeCount").getAsLong(); + expiredDate = jo.get("expiredDate").getAsLong(); + } catch (Exception e) { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":0,\"dueDate\":\"已过期\"}"); + return; + } + if (expiredDate == 0 || nodeCount == 0) { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":0,\"dueDate\":\"已过期\"}"); + return; + } + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + boolean verify = + SM2Util.verify(licencePub, content.getBytes(), ByteUtils.fromHexString(sign)); + if (verify) { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":" + + nodeCount + + ",\"dueDate\":\"" + + format.format(new Date(expiredDate)) + + "\"}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[listLicence:time]" + (end - start)); + } else { + resultCallback.onResult( + "{\"action\":\"onListLicence\",\"nodeCount\":0,\"dueDate\":\"已过期\"}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[listLicence:time]" + (end - start)); + } + } + + @Action(userPermission = 1L << 2L) + public void updateLicence(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + String sign = json.get("sign").getAsString(); + String content = json.get("content").getAsString(); + if (StringUtil.isNullOrEmpty(sign) || StringUtil.isNullOrEmpty(content)) { + resultCallback.onResult( + "{\"action\":\"onUpdateLicence\",\"data\":\"missing signature or content\"}"); + return; + } + long expiredDate; + long nodeCount; + String uuid; + try { + JsonObject jo = JsonParser.parseString(content).getAsJsonObject(); + nodeCount = jo.get("nodeCount").getAsLong(); + expiredDate = jo.get("expiredDate").getAsLong(); + uuid = jo.get("uuid").getAsString(); + + } catch (Exception e) { + resultCallback.onResult("{\"action\":\"onUpdateLicence\",\"data\":\"content error\"}"); + return; + } + if (expiredDate == 0 || nodeCount == 0) { + resultCallback.onResult( + "{\"action\":\"onUpdateLicence\",\"data\":\"parse expiredDate/nodeCount error\"}"); + return; + } + if (!uuid.equals( + ByteUtil.encodeBASE64(HardwareInfo.getCPUID().getBytes()).replaceAll("\n", ""))) { + resultCallback.onResult( + "{\"action\":\"onUpdateLicence\",\"data\":\"invalid licence\"}"); + return; + } + boolean verify = + SM2Util.verify(licencePub, content.getBytes(), ByteUtils.fromHexString(sign)); + + if (verify) { + KeyValueDBUtil.instance.setValue( + NCTables.ConfigDB.toString(), Licence, json.toString()); + resultCallback.onResult("{\"action\":\"onUpdateLicence\",\"data\":\"success\"}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[listLicence:time]" + (end - start)); + return; + } else resultCallback.onResult("{\"action\":\"onUpdateLicence\",\"data\":\"failed\"}"); + long end = System.currentTimeMillis(); + LOGGER.debug("[updateLicence:time]" + (end - start)); + } + + @Action(userPermission = 1 << 9) + public void getOtherNC(JsonObject json, ResultCallback resultCallback) { + Map result = new HashMap<>(); + result.put("action", "onGetOtherNC"); + String s = OtherNCProxy.instance.getOtherNCs().replace(";", " "); + result.put("address", s); + resultCallback.onResult(JsonUtil.toJson(result)); + } + + // 改为NodeManager权限 + @Action(userPermission = 1 << 9) + public void changeOtherNC(JsonObject json, ResultCallback resultCallback) { + String add = json.get("data").getAsString().replace(" ", ";"); + String s = OtherNCProxy.instance.setOtherNCs(add).replace(";", " "); + + Map result = new HashMap<>(); + result.put("action", "onGetOtherNC"); + result.put("address", s); + resultCallback.onResult(JsonUtil.toJson(result)); + } + + // NodeManager权限 + @Action(userPermission = 1 << 9) + public void changeNCFile(JsonObject json, ResultCallback resultCallback) { + String add = json.get("data").getAsString(); + String s = OtherNCProxy.instance.setNCFile(add); + + Map result = new HashMap<>(); + result.put("action", "onGetNCFile"); + result.put("fileName", s); + resultCallback.onResult(JsonUtil.toJson(result)); + } + + @Action(userPermission = 1 << 9) + public void getNCFile(JsonObject json, ResultCallback resultCallback) { + String s = OtherNCProxy.instance.getNCFile(); + + Map result = new HashMap<>(); + result.put("action", "onGetNCFile"); + result.put("fileName", s); + resultCallback.onResult(JsonUtil.toJson(result)); + } + + // TODO 迁移合约实例(从节点A将合约M的实例迁移到节点B)的权限? + /* + * now only support Sole contract type + */ + @Action(userPermission = 0) + public void transferContractInstance(JsonObject json, ResultCallback resultCallback) { + LOGGER.info("transferContractInstance"); + + String node1ID = json.get("node1ID").getAsString(); + String node2ID = json.get("node2ID").getAsString(); + String contractID = json.get("contractID").getAsString(); + + CMNode node1 = NodeCenterActions.nodeInfos.get(node1ID); + if (node1 == null) { + LOGGER.info("node1 " + node1ID + " is null!"); + return; + } + if (node2ID == null) { + LOGGER.info("node2 " + node1ID + " is null!"); + return; + } + if (node1ID.equals(node2ID)) { + LOGGER.info("node1 and node2 is the same node."); + return; + } + + Map request = new HashMap<>(); + request.put("action", "transferInstance"); + request.put("contractID", contractID); + request.put("nodeID", node2ID); + CMNode node2 = NodeCenterActions.nodeInfos.get(node2ID); + if (node2 == null) { + LOGGER.info("node " + node2ID + " is null!"); + return; + } + request.put("address", node2.masterAddress); + node1.connection.controller.sendMsg(JsonUtil.toJson(request)); + + Map result = new HashMap<>(); + result.put("action", "onTransfer"); + result.put( + "data", + "now start transfer contract " + + contractID + + " from node:" + + node1ID + + " to node:" + + node2ID); + resultCallback.onResult(JsonUtil.toJson(result)); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCTables.java b/src/main/java/org/bdware/server/nodecenter/NCTables.java new file mode 100644 index 0000000..35e6a20 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCTables.java @@ -0,0 +1,21 @@ +package org.bdware.server.nodecenter; + +public enum NCTables { + NodeUser, + NodeTime, + ApplyRole, + ApplyTime, + NodeHttpLog, + ContractMeta, + ConfigDB, + NodeTcpLog, + NodesDB, + TrustUnitsDB, + OtherNCs, + NCFile, + ApplyNodeManager; + + public String toString() { + return "NC_" + super.toString(); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NCUDPRunner.java b/src/main/java/org/bdware/server/nodecenter/NCUDPRunner.java new file mode 100644 index 0000000..1ad3bc4 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NCUDPRunner.java @@ -0,0 +1,122 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.udp.UDPMessage; +import org.bdware.sc.udp.UDPNode; + +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.Action; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NCUDPRunner extends Thread { + private static final Logger LOGGER = LogManager.getLogger(NCUDPRunner.class); + public static int mainPort; + public ExecutorService executor = Executors.newFixedThreadPool(5); + public int port; + boolean ready = false; + Map id2IP; + private DatagramSocket socket; + + public NCUDPRunner(Map id2ip2) { + // TODO Auto-generated constructor stub + this.id2IP = id2ip2; + } + + public void run() { + // TimerTask task = new TimerTask() { + // + // @Override + // public void run(Timeout arg0) throws Exception { + // Set nodes = new HashSet(); + // for (Integer i:id2IP.keySet()) { + // UDPNode node = id2IP.get(i); + // if (System.currentTimeMillis()-node.lastUpdatedTime<60*1000) { + // nodes.remove(i); + // } + // } + // for (Integer i:nodes) + // node.re + // } + // }; + id2IP = new HashMap(); + int startPort = mainPort; + while (true) { + try { + socket = new DatagramSocket(startPort); + break; + } catch (Exception e) { + startPort++; + } + } + this.port = startPort; + ready = true; + while (true) { + try { + final DatagramPacket request = new DatagramPacket(new byte[1024], 1024); + socket.receive(request); + final UDPMessage msg = UDPMessage.parse(request.getData(), request.getLength()); + executor.execute(() -> { + try { + onUDPMessage(msg, request); + } catch (Exception e) { + System.err.println("Excpetion in:" + port); + e.printStackTrace(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private void onUDPMessage(UDPMessage msg, DatagramPacket request) { + switch (msg.type) { + case shakeHand: + LOGGER.debug( + "[NCUDPRunner] shakeHand:" + + request.getAddress().getHostAddress() + + ":" + + request.getPort()); + id2IP.put(msg.id, new UDPNode(request.getSocketAddress())); + default: + } + } + + @Action + public void listUDPNodes(JsonObject json, ResultCallback rc) { + Map ret = new HashMap(); + ret.put("action", "onListUDPNodes"); + for (Integer i : id2IP.keySet()) { + UDPNode node = id2IP.get(i); + InetSocketAddress address = (InetSocketAddress) (node.addr); + ret.put("id_" + i, address.getHostString() + ":" + address.getPort()); + } + ret.put("udpPort", port + ""); + + rc.onResult(JsonUtil.toJson(ret)); + } + + public String getUDPAddr(Integer udpid) { + LOGGER.debug(udpid); + UDPNode node = id2IP.get(udpid); + if (node != null) { + InetSocketAddress address = (InetSocketAddress) (node.addr); + return address.getHostString() + ":" + address.getPort(); + } + return null; + } + + public void remove(String udpID) { + id2IP.remove(Integer.valueOf(udpID)); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NameAndID.java b/src/main/java/org/bdware/server/nodecenter/NameAndID.java new file mode 100644 index 0000000..7050d51 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NameAndID.java @@ -0,0 +1,5 @@ +package org.bdware.server.nodecenter; + +public class NameAndID { + String name,id; +} diff --git a/src/main/java/org/bdware/server/nodecenter/NodeCenterActions.java b/src/main/java/org/bdware/server/nodecenter/NodeCenterActions.java new file mode 100644 index 0000000..a31f75e --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NodeCenterActions.java @@ -0,0 +1,1783 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.*; +import com.google.gson.reflect.TypeToken; +import io.netty.util.Timeout; +import io.netty.util.TimerTask; +import io.netty.util.internal.StringUtil; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.ContractResult; +import org.bdware.sc.ContractResult.Status; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.bean.ContractExecType; +import org.bdware.sc.bean.ContractRequest; +import org.bdware.sc.conn.ByteUtil; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.encrypt.HardwareInfo; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.*; +import org.bdware.server.permission.Role; +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.zz.gmhelper.SM2Util; + +import java.io.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class NodeCenterActions { + // static SyncResult syncResult = new SyncResult(); + private static final Logger LOGGER = LogManager.getLogger(NodeCenterActions.class); + private static final String MISSING_ARGUMENT = "Missing arguments"; + public static Map nodeInfos = + new ConcurrentHashMap<>(); // all online nodes,key是节点pubKey + + public static SyncResult sync = new SyncResult(); + + public static Map contractID2Members = + new ConcurrentHashMap<>(); // contractID,list(nodes' pubKey) + // public static ConcurrentHashMap> + // recoverMap = + // new ConcurrentHashMap<>(); // k-nodesPubKey,v-(k-contractID),only unit contract + // TODO 定期清缓存 + // public static Map requestCache = + // new ConcurrentHashMap<>(); // key is contractID,only for requestAll type contract + static TimerTask checkAliveTask; + static Timeout checkAliveTimeout; + + static { + int delay = 1000; + checkAliveTask = + new TimerTask() { + @Override + public void run(Timeout arg0) { + try { + if (!arg0.isCancelled()) { + checkAlive(); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (!arg0.isCancelled()) { + checkAliveTimeout = + SyncResult.timer.newTimeout(this, delay, TimeUnit.MILLISECONDS); + } + } + + // heartbeat?(zyx) + private void checkAlive() { + Set toRemove = new HashSet<>(); + for (String key : nodeInfos.keySet()) { + if (!nodeInfos.get(key).checkAlive()) { + LOGGER.info("Node " + key.substring(0, 5) + " is offline!"); + // LOGGER.info( + // "[NodeCenterActions] 发现节点 + // " + // + key.substring(0, + // 5) + // + " not alive!"); + toRemove.add(key); + } + } + + for (String key : toRemove) { + CMNode info = nodeInfos.get(key); + LOGGER.debug("Heart beat: " + info.nodeName + " is offline!"); + // System.out.println("[ADSP]NC心跳机制发现节点 " + + // info.nodeName + " 下线!"); + + synchronized (info) { + if (null != info.contracts && !info.contracts.isEmpty()) { + for (ContractDesp cd : info.contracts) { + cd.setIsMaster(false); + LOGGER.info( + "checkAlive---- 设置节点 " + + info.pubKey.substring(0, 5) + + " 的合约 " + + cd.contractID + + " isMaster=" + + false); + } + } + } + + nodeInfos.remove(key); + try { + info.connection.controller.ctx.close(); + + } catch (Exception ignored) { + } + + /* for(String contractID : recoverMap.get(info.pubKey).keySet()){ + MasterActions.unitModeCheck(contractID); + ContractRecord record = recoverMap.get(info.pubKey).get(contractID); + synchronized (record) { + if (record.recoverFlag == RecoverFlag.Fine) + record.recoverFlag = RecoverFlag.ToRecover; + } + }*/ + } + } + }; // checkAliveTask + checkAliveTimeout = + SyncResult.timer.newTimeout(checkAliveTask, delay, TimeUnit.MILLISECONDS); + + // checkAliveTimeout = SyncResult.timer.newTimeout(checkAliveTask, 20, TimeUnit.SECONDS); + + // NodeCenterServer.scheduledThreadPool.scheduleWithFixedDelay( + // () -> { + // boolean flag = clearCache(); + // if (flag) { + // try { + // Thread.sleep(10000L); + // } catch (InterruptedException e) { + // LOGGER.error("sleeping is interrupted! " + e.getMessage()); + // } + // } + // }, + // 0, + // 0, + // TimeUnit.SECONDS); + } + + public NodeCenterFrameHandler controller; + String nodeID; // node pubKey + boolean NCConFlag = false; // true表示这个是别的NC连过来的 + Map fileMap = new HashMap<>(); + private String sessionID; + private String nodeManagerPubkey; + + public NodeCenterActions(NodeCenterFrameHandler nodeCenterFrameHandler) { + controller = nodeCenterFrameHandler; + } + + public static ContractDesp getContractByID(String key) { + for (CMNode node : nodeInfos.values()) { + for (ContractDesp desp : node.contracts) { + if (desp.contractID.equals(key)) return desp; + } + } + return null; + } + + public static ContractDesp getContractByName(String key) { + for (CMNode node : nodeInfos.values()) { + for (ContractDesp desp : node.contracts) { + if (desp.contractID.equals(key) || desp.contractName.equals(key)) return desp; + } + } + return null; + } + + public static String getPeerIdByNodeId(String id) { + CMNode node = nodeInfos.get(id); + return node.peerID; + } + + @Action(httpAccess = true) + public void downloadUUID(JsonObject args, ResultCallback resultCallback) { + String pubKey = args.get("pubKey").getAsString(); + String signature = args.get("sign").getAsString(); + String nonce = args.get("nonce").getAsString(); + if (StringUtil.isNullOrEmpty(pubKey) + || StringUtil.isNullOrEmpty(signature) + || StringUtil.isNullOrEmpty(nonce)) { + resultCallback.onResult("missing pubkey/sign/nonce"); + } + + if (NCManagerAction.isCenterManager(pubKey)) { + + boolean verify = SM2Util.plainStrVerify(pubKey, nonce, signature); + if (!verify) { + resultCallback.onResult("verify failed"); + return; + } + String uuid = + ByteUtil.encodeBASE64(HardwareInfo.getCPUID().getBytes()).replaceAll("\n", ""); + ((HttpResultCallback) resultCallback) + .addHeader("content-disposition", "attachment;filename=encodeduuid.key"); + resultCallback.onResult(uuid); + } else { + resultCallback.onResult("permission denied, not ContractManager"); + } + } + + private Response listCMInfoInterval(JsonObject arg) { + Response r = new Response(); + r.action = "onListCMInfo"; + r.data = new HashMap<>(nodeInfos); + if (arg.has("requestID")) { + r.responseID = arg.get("requestID").getAsString(); + } + return r; + } + // + // @Action(userPermission = 0) + // public void login(JsonObject json, ResultCallback resultCallback) { + // long start = System.currentTimeMillis(); + // if (sessionID == null) + // resultCallback.onResult("{\"action\":\"onLogin\",\"data\":\"failed\"}"); + // String signature = json.get("signature").getAsString(); + // String pubKey = json.get("pubKey").getAsString(); + // SM2 sm2 = new SM2(); + // boolean result = + // sm2.verify( + // (sessionID), + // Signature.loadFromString(signature), + // "", + // SM2KeyPair.publicKeyStr2ECPoint(pubKey)); + // logger.debug("session:" + (sessionID)); + // if (result) { + // this.nodeManagerPubkey = pubKey; + // logger.debug("设置公钥" + pubKey); + // resultCallback.onResult("{\"action\":\"onLogin\",\"data\":\"success\"}"); + // } else { + // resultCallback.onResult("{\"action\":\"onLogin\",\"data\":\"failed\"}"); + // } + // long l = getPermission(nodeManagerPubkey); + // controller.setPermission(l); + // long end = System.currentTimeMillis(); + // logger.debug("time:" + (end - start)); + // } + + private void sendContractResult(JsonObject json) { + controller.sendMsg(json.toString()); + } + + @Action + public void syncPing(JsonObject json, ResultCallback rc) { + // logger.debug("[NodeCenterAction] syncPing"); + if (json.has("data")) { + String data = json.get("data").getAsString(); + LOGGER.debug("[syncPing] data" + data); + CMNode node; + node = nodeInfos.get(json.get("id").getAsString()); + switch (data.charAt(0)) { + case '+': + node.addCIManager(data.substring(1)); + break; + case '-': + node.removeCIManager(data.substring(1)); + break; + case '@': // peerID + node.peerID = data.substring(1); + break; + default: + LOGGER.debug("[syncPing] wrong data" + data); + } + LOGGER.debug("[syncPing] info change"); + } + if (json.has("contractVersion")) { + CMNode node = nodeInfos.get(nodeID); + // LOGGER.debug("[syncPing] contractVersion:"+json.get("contractVersion").getAsInt()+" + // -- "+node.contractVersion); + + if (null != node && json.get("contractVersion").getAsInt() != node.contractVersion) { + rc.onResult("{\"action\":\"syncContractInfo\"}"); + } + } + rc.onResult( + "{\"action\":\"syncPong\",\"requestID\":\"" + + json.get("requestID").getAsString() + + "\"}"); + } + + @Action(userPermission = 0) + public void getSessionID(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + sessionID = new Random().nextLong() + "_session"; + Map result = new HashMap<>(); + result.put("action", "onSessionID"); + result.put("session", sessionID); + resultCallback.onResult(JsonUtil.toJson(result)); + long end = System.currentTimeMillis(); + LOGGER.debug("cmclient time:" + (end - start) + "data:" + JsonUtil.toJson(result)); + } + + private long getPermission(String pubKey) { + if (pubKey == null) { + return 0L; + } + try { + String ret; + ret = KeyValueDBUtil.instance.getValue(NCTables.NodeUser.toString(), pubKey); + LOGGER.info("Role=" + ret + " --> pubkey=" + pubKey); + if (null == ret || ret.isEmpty()) { + /* + KeyValueDBUtil.instance.setValue( + NCTables.NodeUser.toString(), + pubKey, + Role.Node.toString()); + KeyValueDBUtil.instance.setValue( + NCTables.NodeTime.toString(), + pubKey, + Long.toString(new Date().getTime())); + return Role.Node.getValue(); + */ + // TODO fix permission bugs. + return Role.compoundValue(new String[] {"NodeManager"}); + } else { + + return (Role.compoundValue(ret.split(","))); + } + } catch (Exception e) { + e.printStackTrace(); + return 0L; + } + } + + @Action(userPermission = 0) + public void setNodeID(JsonObject json, ResultCallback rc) { + LOGGER.debug(json.toString()); + if (rc instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "setNodeID"; + r.data = "forbidden"; + rc.onResult(JsonUtil.toJson(r)); + return; + } + Response r = new Response(); + r.action = "onSetNodeID"; + String pubkey = json.get("id").getAsString(); + String signature = json.get("signature").getAsString(); + LOGGER.debug("id:" + pubkey + " sig:" + signature); + r.data = "failed"; + if (sessionID != null && SM2Util.plainStrVerify(pubkey, pubkey + sessionID, signature)) { + nodeID = pubkey; + controller.pubKey = nodeID; + CMNode node = new CMNode(this, pubkey); + String nodeShortID = node.pubKey.substring(0, 5); + if (json.has("cimanager")) { + node.setCIManager(json.get("cimanager").getAsString()); + } + if (json.has("peerID")) { + node.setPeerID(json.get("peerID").getAsString()); + } + if (json.has("ipPort")) { + node.setIpPort(json.get("ipPort").getAsString()); + } + long permission = getPermission(pubkey); + if (permission != 0) { + controller.setPermission(permission); + LOGGER.info("node " + nodeShortID + " is online!"); + nodeInfos.put(pubkey, node); + if (json.has("nodeName")) { + node.nodeName = json.get("nodeName").getAsString(); + KeyValueDBUtil.instance.setValue( + NCTables.NodesDB.toString(), pubkey, node.nodeName); + LOGGER.info("set node name: " + nodeShortID + " -> " + node.nodeName); + } + if (json.has("masterAddress")) { + node.masterAddress = json.get("masterAddress").getAsString(); + } + + r.data = "success"; + + if (null != KeyValueDBUtil.instance.getValue(NCTables.NodesDB.toString(), pubkey)) { + KeyValueDBUtil.instance.setValue(NCTables.NodesDB.toString(), pubkey, pubkey); + } + // Map udpReq = new HashMap<>(); + // udpReq.put("action", "getUDPID"); + /// udpReq.put("udpPort", runner.port + ""); + // rc.onResult(JsonUtil.toJson(udpReq)); + } + } + + // TODO 如果是NC挂了,其他节点重连之后需要另外考虑 + // 向该合约发送请求,令其查看自己崩溃前的集群合约 + LOGGER.info("NodeCenter tells nodes new masters for contracts created before the crash!"); + Map request = new HashMap<>(); + request.put("action", "queryUnitContractsID"); + try { + CMNode conn = nodeInfos.get(pubkey); + NodeCenterActions connection = conn.connection; + NodeCenterFrameHandler connController = connection.controller; + connController.sendMsg(JsonUtil.toJson(request)); + } catch (Exception e) { + e.printStackTrace(); + } + Map onNewNodeConnected = new HashMap<>(); + onNewNodeConnected.put("action", "onNewNodeConnected"); + onNewNodeConnected.put("pubKey", pubkey); + for (CMNode node : nodeInfos.values()) { + if (node.pubKey.equals(pubkey)) { + continue; + } + try { + node.connection.controller.sendMsg(JsonUtil.toJson(onNewNodeConnected)); + } catch (Exception e) { + e.printStackTrace(); + } + } + rc.onResult(JsonUtil.toJson(r)); + } + + @Action(userPermission = 0) + public void getNCConnectPermission(JsonObject json, ResultCallback rc) { + LOGGER.debug("[NodeCenterAction] getNCConnectPermission " + json.toString()); + if (rc instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "onGetNCConnectPermission"; + r.data = "forbidden"; + rc.onResult(JsonUtil.toJson(r)); + return; + } + Response r = new Response(); + r.action = "onGetNCConnectPermission"; + String pubkey = json.get("id").getAsString(); + String signature = json.get("signature").getAsString(); + LOGGER.debug("[NodeCenterController] id:" + pubkey + " sig:" + signature); + if (sessionID != null && SM2Util.plainStrVerify(pubkey, pubkey + sessionID, signature)) { + r.data = "success"; + NCConFlag = true; + } else r.data = "failed"; + + rc.onResult(JsonUtil.toJson(r)); + } + + @Action(httpAccess = true) + public void listCMInfo(JsonObject json, ResultCallback rc) { + // 检查是否为在线节点 + // if (!json.containsKey("pubKey") || !nodeinfos.containsKey(json.get("pubKey"))) { + // LOGGER.error("node pubKey not valid " + json.get("pubKey")); + // return; + // } + Response r = listCMInfoInterval(json); + rc.onResult(JsonUtil.toJson(r)); + } + + @Action(async = true, userPermission = 1) + public void checkIsContract(JsonObject json, ResultCallback rc) { + JsonArray array = json.get("list").getAsJsonArray(); + JsonObject jo = new JsonObject(); + jo.addProperty("action", "onCheckIsContract"); + JsonObject jo1 = new JsonObject(); + for (int i = 0; i < array.size(); i++) { + String key = array.get(i).getAsString(); + String val = KeyValueDBUtil.instance.getValue(NCTables.ContractMeta.toString(), key); + jo1.addProperty(key, val != null && val.length() > 0); + } + jo.addProperty("responseID", json.get("requestID").getAsString()); + jo.add("result", jo1); + rc.onResult(jo.toString()); + } + + @Action(async = true, userPermission = 1L) + public void updateContract(JsonObject json, ResultCallback rc) throws IOException { + // TODO @fanbo 收到了更新请求时,检查本地数据库,看是否有contractID对应的索引。 + // 如果没有,就rc.onResult("{\"action\":\"requestReadMe\",\"contractID\":\"xxx\"}"); + CMNode node = nodeInfos.get(nodeID); + if (null != node) { + // 对与旧的master挂了,但是合约进程没挂,旧master重新上线后需要看现在该合约是否有新的master + // 如果有就更改json串,并发消息给该节点将Contract的isMaster设置为false + Set contractIDS = new HashSet<>(); // 旧master需要自己非master的合约 + // 当前节点的合约 + List contracts = + JsonUtil.fromJson( + json.get("contracts"), + new TypeToken>() {}.getType()); + MetaIndexAction.updateContractsIndex(contracts, rc); + LOGGER.debug("update contracts: " + json.get("contracts")); + int version = -1; + if (json.has("contractVersion")) { + version = json.get("contractVersion").getAsInt(); + } + node.updateContract(contracts, version); + if (json.has("events")) { + node.events = json.get("events").getAsInt(); + } + // 遍历所有节点 + for (CMNode cmNode : nodeInfos.values()) { + if (cmNode.pubKey.equals(nodeID)) { + continue; + } + if (null == cmNode.contracts || cmNode.contracts.isEmpty()) { + continue; + } + // 遍历其他节点的所有合约 + for (ContractDesp desp : cmNode.contracts) { + if (desp.type == ContractExecType.Sole) { + continue; + } + // 遍历当前节点的所有合约 + for (ContractDesp thisDesp : contracts) { + /// logger.debug("测试位置555 节点" + cmNode.pubKey.substring(0,5) + " + // 的合约 " + desp.contractID + " " + desp.isMaster + " 与 节点 " + + // nodeID.substring(0,5) + " 的合约 " + thisDesp.contractID + " " + + // thisDesp.isMaster); + + if (!((desp.contractID).equals(thisDesp.contractID))) { + continue; + } + if (thisDesp.type == ContractExecType.Sole) { + continue; + } + + if (desp.getIsMaster() && thisDesp.getIsMaster()) { + LOGGER.info( + "合约 " + + thisDesp.contractID + + " 的旧的master " + + node.pubKey.substring(0, 5) + + "重新上线后和现在master " + + cmNode.pubKey.substring(0, 5) + + "撞车!"); + contractIDS.add(thisDesp.contractID); + } + } + } + } + + StringBuilder con_ids = new StringBuilder(); + for (ContractDesp cd : contracts) { + if (contractIDS.contains(cd.contractID)) { + cd.setIsMaster(false); + LOGGER.info( + "设置合约 " + + cd.contractID + + " 的旧master " + + node.pubKey.substring(0, 5) + + " isMaster为false!"); + con_ids.append(",").append(cd.contractID); + } + } + if (!con_ids.toString().equals("")) { + Map request = new HashMap<>(); + request.put("action", "updateNonMasters"); + request.put("contracts", con_ids.toString()); + node.connection.controller.sendMsg(JsonUtil.toJson(request)); + } + if (rc instanceof HttpResultCallback) { + rc.onResult("{\"msg\":\"success\"}"); + } + } + } + + @Action(async = true, userPermission = 1) + public void checkAlive(JsonObject json, ResultCallback rc) { + checkAliveTimeout.cancel(); + checkAliveTimeout = SyncResult.timer.newTimeout(checkAliveTask, 0, TimeUnit.SECONDS); + rc.onResult("{\"action\":\"pong\",\"data\":" + System.currentTimeMillis() + "}"); + } + + @Action(async = true, userPermission = 1L << 27) + public void takeScreenshot(JsonObject json, ResultCallback rc) { + String nodeName = json.get("nodeName").getAsString(); + if (nodeName == null) { + rc.onResult("{\"status\":\"error\",\"data\":\"missing argument: nodeName\"}"); + return; + } + for (CMNode node : nodeInfos.values()) { + if (node.nodeName.equals(nodeName) + || node.pubKey.equals(nodeName) + || node.udpID.equals(nodeName) + || node.ipPort.equals(nodeName)) { + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + Map req = new HashMap<>(); + req.put("requestID", requestID); + req.put("action", "takeScreenshot"); + node.connection.controller.sendMsg(JsonUtil.toJson(req)); + sync.sleepWithTimeout(requestID, rc, 20); + return; + } + } + rc.onResult("{\"status\":\"error\",\"data\":\"can't local device!\"}"); + } + + @Action(async = true) + public void onTakeScreenshot(JsonObject json, ResultCallback rc) { + sync.wakeUp(json.get("responseID").getAsString(), json.get("data").getAsString()); + } + + @Action(async = true, userPermission = 1) + public void onReceiveContractExecution(JsonObject json, ResultCallback rc) { + if (rc instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "onReceiveContractExecution"; + r.data = "forbidden"; + rc.onResult(JsonUtil.toJson(r)); + return; + } + String requesterID = json.get("requesterNodeID").getAsString(); + CMNode node = nodeInfos.get(requesterID); + if (node != null) node.connection.sendContractResult(json); + if (requesterID.equals("NodeCenter")) { + sync.wakeUp( + json.get("requestID").getAsString(), json.get("contractResult").getAsString()); + } + } + + @Action(async = true, userPermission = 1) + public void executeContractOnOtherNodes(JsonObject json, ResultCallback rc) { + if (rc instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "onExecuteContractOnOtherNodes"; + r.data = "forbidden"; + rc.onResult(JsonUtil.toJson(r)); + return; + } + LOGGER.debug( + "tid:" + + Thread.currentThread().getId() + + "executeContractOnOtherNodes\n" + + JsonUtil.toJson(json)); + String requestID = json.get("requestID").getAsString(); + try { + CMNode node; + String crStr = json.get("contractRequest").getAsString(); + ContractRequest cr = JsonUtil.fromJson(crStr, ContractRequest.class); + String requesterNodeID = nodeID; + for (String nodeID : nodeInfos.keySet()) { + if (nodeID.equals(this.nodeID)) continue; + node = nodeInfos.get(nodeID); + if (node != null && node.contracts != null) + for (ContractDesp cd : node.contracts) { + if (cr.getContractID().equals(cd.contractID) + || cr.getContractID().equals(cd.contractName)) { + JsonObject jo = new JsonObject(); + jo.addProperty("requestID", requestID); + jo.addProperty("requesterNodeID", requesterNodeID); + jo.addProperty("action", "executeContractLocally"); + jo.addProperty("contractRequest", crStr); + LOGGER.debug( + "tid:" + + Thread.currentThread().getId() + + "execute in node:" + + node.nodeName); + node.connection.controller.sendMsg(JsonUtil.toJson(jo)); + return; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + LOGGER.debug("tid:" + Thread.currentThread().getId() + "can't locate in NodeCenter!"); + ContractResult cResult = + new ContractResult( + Status.Error, + new JsonPrimitive("can't locate the Contract in node center")); + JsonObject jo = new JsonObject(); + jo.addProperty("action", "onReceiveContractExecution"); + jo.addProperty("requestID", requestID); + jo.addProperty("contractResult", JsonUtil.toJson(cResult)); + rc.onResult(JsonUtil.toJson(jo)); + } + + @Action(async = true) + public void deliverEMessage(JsonObject json, ResultCallback cb) { + if (cb instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "deliverEvent"; + r.data = "forbidden"; + cb.onResult(JsonUtil.toJson(r)); + return; + } + + CMNode node = nodeInfos.get(json.get("target").getAsString()); + node.sendEMsg(json.get("msg").getAsString()); + } + + @Action + public void getLogSize(JsonObject json, ResultCallback cb) { + syncRequestCM(json, cb); + } + + @Action + public void requestLog(JsonObject json, ResultCallback cb) { + syncRequestCM(json, cb); + } + + @Action(userPermission = 2) + public void requestLastLog(JsonObject json, ResultCallback cb) { + syncRequestCM(json, cb); + } + + private void syncRequestCM(JsonObject json, final ResultCallback cb) { + String requestID = System.currentTimeMillis() + " | " + Math.random() * 1000; + json.addProperty("requestID", requestID); + Map response = new HashMap<>(); + response.put("status", "Error"); + if (!json.has("contractID")) { + response.put("result", "missing argument: contractID"); + cb.onResult(JsonUtil.toJson(response)); + return; + } + boolean hasContract = false; + for (CMNode node : nodeInfos.values()) { + if (node.containsContract(json.get("contractID").getAsString())) { + hasContract = true; + node.connection.controller.sendMsg(JsonUtil.toJson(json)); + break; + } + } + if (!hasContract) { + response.put("data", "can not locate contractID: " + json.get("contractID")); + cb.onResult(JsonUtil.toJson(response)); + } + sync.sleep( + requestID, + new ResultCallback() { + @Override + public void onResult(String str) { + ContractResult ret = JsonUtil.fromJson(str, ContractResult.class); + Map converted = new HashMap<>(); + converted.put("status", ret.status); + try { + List> je = + JsonUtil.fromJson( + ret.result, + new TypeToken< + List>>() {}.getType()); + converted.put("result", je); + } catch (Exception e) { + converted.put("result", ret.result); + } + String retStr = JsonUtil.toJson(converted); + cb.onResult(retStr); + } + }); + } + + /* // judge the just online node whwther need to restart contracts,send the contracts'info + public static void restartContracts(String nodePubKey) { + if (!recoverMap.containsKey(nodePubKey)) { + return; + } + + // 恢复该节点的每一个集群运行的合约 + for (ContractRecord record : recoverMap.get(nodePubKey).values()) { + String contractID = record.contractID; + if (record.recoverFlag != RecoverFlag.ToRecover) continue; + + // 先发消息,让恢复节点的该合约收到消息后只加入队列不dealRequests + Map request = new HashMap<>(); + request.put("action", "setRecovering"); + request.put("contractID", contractID); + CMNode node = nodeinfos.get(nodePubKey); + node.connection.controller.sendMsg(gson.toJson(request)); + + System.out.println( + "第一步 : [NodeCeterActions] restartContracts 开始处理合约 contractID=" + contractID); + + if (contractID2Members.containsKey(contractID)) { + + // 在nodeinfos中找节点,该节点的pubKey在pubKeys中 + MultiPointContractInfo info = contractID2Members.get(contractID); + CMNode cmNode = null; + for (int i = 0; i < info.members.size(); i++) { + int size = info.members.size(); + String tempNodeID = info.members.get(record.order.incrementAndGet() % size); + + if (nodeinfos.containsKey(tempNodeID)) + cmNode = NodeCenterActions.nodeinfos.get(tempNodeID); + else continue; + + System.out.println("查询节点 " + cmNode.nodeName); + + if (cmNode != null && !cmNode.pubKey.equals(nodePubKey)) { + System.out.println("第二步 : [NodeCenterActions] 找到一个依赖恢复节点,其节点名为 " + + cmNode.nodeName); + + Map req = new HashMap(); + req.put("action", "dumpCurrentState"); + req.put("contractID", contractID); + req.put("targetNodePubkey", nodePubKey); + + // NC向该节点发送请求,让其存储自身当前状态并发给NC + cmNode.connection.controller.sendMsg(gson.toJson(req)); + + return; + } + } + + if (cmNode == null) { + logger.debug("[NodeCenterActions] Can't find a recover rely node!"); + } + } + } + }*/ + + // zyx modified + /* @Action(async = true) + public void receiveState(Map args, final ResultCallback rc) { + //System.out.println("第五步 : [NodeCenterActions] 开始从恢复依赖节点接收state"); + + String fileName = args.get("fileName") + "_NC"; + boolean isAppend = Boolean.valueOf(args.get("isAppend")); + boolean isDone = Boolean.valueOf(args.get("isDone")); + + //logger.debug("[NodeCenterActions] isAppend=" + isAppend + " isDone=" + isDone); + + String path = "./temp/stateFiles/" + fileName; + File file = new File(path); + File dir = file.getParentFile(); + if (!dir.exists()) { + dir.mkdirs(); + } + + FileOutputStream fout = null; + if (!isAppend) { + try { + fout = new FileOutputStream(file); + fileMap.put(fileName, fout); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } else { + fout = fileMap.get(fileName); + } + + if (isDone) { + if (fout != null) { + try { + fout.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + // TODO 参数可以加入文件名? + //System.out.println("第七步 : [NodeCenterActions] 从恢复依赖节点接收state完成"); + + //logger.debug("[NodeCenterActions] receive state finish."); + String receiveFileName = new File(dir, fileName).getAbsolutePath(); + + // 发送state给需要恢复的节点 + sendState(fileName, args.get("targetNodePubkey")); + } else { + String data = args.get("data"); + try { + String progress = args.get("progress"); + // TODO pulish progress + fout.write(ByteUtil.decodeBASE64(data)); + } catch (IOException e) { + e.printStackTrace(); + } + } + }*/ + + // The resultCallback is an Counter, it will invoke + // in its + // onResult + // zyx modified + /* public void sendState(String path, String targetNodePubkey) { + File project = new File("./temp/stateFiles/" + path); + + // 将合约中的memberd加入ContractRecord + ContractRecord record = null; + try { + FileInputStream fileout = new FileInputStream(project); + GZIPInputStream gzin = new GZIPInputStream(fileout); + ObjectInputStream reader = new ObjectInputStream(gzin); + record = (ContractRecord) reader.readObject(); + reader.close(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + // record.members = contractID2MembersUDP.get(record.contractID); + //NC delete state file + if(project.isFile() && project.exists()){ + project.delete(); + } + + record.members = new HashMap<>(recoverMap.get(nodeID).get(record.contractID).members); + + File file = new File("./temp/stateFiles/" + path); + File parent = file.getParentFile(); + if (!parent.exists()) parent.mkdirs(); + ObjectOutputStream writer; + try { + FileOutputStream fileout = new FileOutputStream(file); + GZIPOutputStream out = new GZIPOutputStream(fileout); + writer = new ObjectOutputStream(out); + writer.writeObject(record); + writer.flush(); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + //System.out.println("第八步 : [NodeCenterActions] 开始向需要恢复的节点转发State"); + //record.printContent(); + + CMNode targetNode = nodeinfos.get(targetNodePubkey); + + Map req = new HashMap<>(); + req.put("action", "receiveState"); + req.put("isAppend", "false"); + req.put("fileName", path); + req.put("isDone", "false"); + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + req.put("requestID", requestID); + + if (project.isFile()) { + try { + FileInputStream fin = new FileInputStream(project); + byte[] buff = new byte[30 * 1024]; + long count = 0; + long total = project.length(); + + for (int len = 0; (len = (fin.read(buff))) > 0; ) { + //logger.debug("len = " + len); + String data = ByteUtil.encodeBASE64(buff, len); + req.put("data", data); + count += len; + targetNode.connection.controller.sendMsg(gson.toJson(req)); + req.put("isAppend", "true"); + + Thread.sleep(300); + } + fin.close(); + + req.put("isDone", "true"); + req.remove("data"); + + targetNode.connection.controller.sendMsg(gson.toJson(req)); + //NC delete state file + if(file.isFile() && file.exists()){ + file.delete(); + } + + //logger.debug("[NodeCenterActions] send state project finish."); + + //System.out.println("第十步 : [NodeCenterActions] 向需要恢复的节点转发State完成"); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }*/ + + @Action + public void onReceiveSyncRequest(JsonObject jo, final ResultCallback cb) { + if (cb instanceof HttpResultCallback) { + Response r = new Response(); + r.action = "pubEvent"; + r.data = "forbidden"; + cb.onResult(JsonUtil.toJson(r)); + return; + } + String data = jo.get("contractResult").getAsString(); + String requestID = jo.get("requestID").getAsString(); + sync.wakeUp(requestID, data); + } + + // userPermission = 256 + @Action(async = true, httpAccess = true, userPermission = 0) + public void executeContract(JsonObject args, final ResultCallback rc) { + final long start = System.currentTimeMillis(); + ContractRequest cr = new ContractRequest(); + + if (!args.has("contractID")) { + rc.onResult(MISSING_ARGUMENT); + return; + } + String contractID = args.get("contractID").getAsString(); + + cr.setContractID(contractID); + if (!args.has("arg")) { + rc.onResult(MISSING_ARGUMENT); + return; + } + if (args.has("operation")) { + cr.setAction(args.get("operation").getAsString()); + cr.setArg(args.get("arg").getAsString()); + } + + final JsonObject ret = new JsonObject(); + + ret.addProperty("action", "onExecuteResult"); + if (args.has("requester")) { + cr.setPublicKey(args.get("requester").getAsString()); + byte[] sign = ByteUtils.fromHexString(args.get("signature").getAsString()); + try { + sign = SM2Util.encodeSM2SignToDER(sign); + } catch (Exception e) { + e.printStackTrace(); + } + cr.setSignature(ByteUtils.toHexString(sign)); + } + if (args.has("pubkey")) { + cr.setPublicKey(args.get("pubkey").getAsString()); + cr.setSignature(args.get("signature").getAsString()); + } + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + String crStr = JsonUtil.toJson(cr); + ret.addProperty("responseID", requestID); + boolean hasResult = false; + + for (String nodeID : nodeInfos.keySet()) { + if (nodeID.equals(this.nodeID)) continue; + CMNode node = nodeInfos.get(nodeID); + if (node != null && node.contracts != null) + for (ContractDesp cd : node.contracts) { + if (cr.getContractID().equals(cd.contractID) + || cr.getContractID().equals(cd.contractName)) { + JsonObject jo = new JsonObject(); + jo.addProperty("requestID", requestID); + jo.addProperty("requesterNodeID", "NodeCenter"); + jo.addProperty("action", "executeContractLocally"); + jo.addProperty("contractRequest", crStr); + node.connection.controller.sendMsg(JsonUtil.toJson(jo)); + sync.sleep( + requestID, + new ResultCallback() { + @Override + public void onResult(String str) { + ContractResult cr = + JsonUtil.fromJson(str, ContractResult.class); + ret.add("result", cr.result); + ret.addProperty("status", cr.status + ""); + long executeTime = (System.currentTimeMillis() - start); + ret.addProperty("executeTime", executeTime + ""); + String retStr = JsonUtil.toJson(ret); + rc.onResult(retStr); + } + }); + hasResult = true; + break; + } + } + } + if (!hasResult) { + ContractResult result = + new ContractResult( + Status.Error, new JsonPrimitive("Can't locate contract At NodeCenter")); + ret.addProperty("result", JsonUtil.toJson(result)); + long executeTime = (System.currentTimeMillis() - start); + ret.addProperty("executeTime", executeTime + ""); + String retStr = JsonUtil.toJson(ret); + rc.onResult(retStr); + } + } + + // zyx modified + @Action(async = true) + public void transmitRecords(JsonObject args, final ResultCallback rc) { + String targetID = args.get("targetNodePubKey").getAsString(); + args.remove("targetNodePubKey"); + args.addProperty("action", "addRequestQueue"); + + CMNode cmNode = NodeCenterActions.nodeInfos.get(targetID); + cmNode.connection.controller.sendMsg(JsonUtil.toJson(args)); + } + + // -------------UDP-------- + @Action + public void onGetUDPID(JsonObject args, final ResultCallback rc) { + String udpID = args.get("udpID").getAsString(); + CMNode info = nodeInfos.get(nodeID); + LOGGER.debug("NC onGetUDPID 节点 " + info.nodeName + " 上线后设置udpID"); + + info.udpID = udpID; + } + + @Action + public void listContractID2Members(JsonObject args, final ResultCallback rc) { + JsonObject jo = new JsonObject(); + jo.addProperty("action", "onListContractID2Members"); + jo.addProperty("status", "Success"); + // jo.addProperty("data", JsonUtil.toJson(contractID2Members)); + rc.onResult(jo.toString()); + } + + @Action(async = true) + public void receiveTrustfullyResult(JsonObject args, final ResultCallback rc) { + LOGGER.debug("tid" + Thread.currentThread().getId() + "reqID" + args.get("responseID")); + sync.wakeUp(args.get("responseID").getAsString(), JsonUtil.toJson(args)); + // we don't need any response. + } + + @Action(async = true) + public void onStartContractTrustfully(JsonObject args, final ResultCallback rc) { + LOGGER.debug("[onStartContractTrustfully] " + args.get("requestID")); + sync.wakeUp(args.get("requestID").getAsString(), JsonUtil.toJson(args)); + // we don't need any response. + } + + // cb will invoke sendProject in the cluster in its onResult method. + public void requestProject( + String projectName, String pubkey, boolean isPrivate, ResultCallback cb) { + LOGGER.debug("[NodeCenterActions] requestProject : -----------position2"); + + Map req = new HashMap<>(); + req.put("action", "sendProject"); + req.put("isPrivate", isPrivate); + req.put("projectName", projectName); + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + req.put("requestID", requestID); + req.put("pubKey", pubkey); + // sync.wakeUp --> receiveProject done; + + sync.sleepWithTimeout(requestID, cb, 60); + controller.sendMsg(JsonUtil.toJson(req)); + } + + // The resultCallback is an Counter, it will invoke + // in its + // onResult + public void sendProject( + String filePath, String isPrivate, String pubKey, ResultCallback result) { + LOGGER.debug("sendProject : position----6" + filePath); + + File project = new File(filePath); + String projectName = project.getName().split("_NC")[0]; + LOGGER.debug("[JJJJJJJ] " + projectName); + Map req = new HashMap<>(); + req.put("action", "receiveProject"); + req.put("isPrivate", isPrivate); + if (isPrivate.equals("true")) { + ; + req.put("pubKey", pubKey); + } + req.put("isAppend", "false"); + req.put("fileName", projectName); + req.put("isDone", "false"); + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + req.put("requestID", requestID); + // sync.wakeup at --> onReceiveProject + sync.sleep(requestID, result); + + if (project.isFile()) { + try { + FileInputStream fin = new FileInputStream(project); + byte[] buff = new byte[30 * 1024]; + long count = 0; + long total = project.length(); + LOGGER.debug("ypk = " + total); + + for (int len = 0; (len = (fin.read(buff))) > 0; ) { + LOGGER.debug("len = " + len); + String data = ByteUtil.encodeBASE64(buff, len); + req.put("data", data); + count += len; + controller.sendMsg(JsonUtil.toJson(req)); + req.put("isAppend", "true"); + + Map req2 = new HashMap<>(); + req2.put("operation", "onDistribute"); + req2.put("requestID", requestID); + req2.put("progress", String.format("%.2f", count * 100F / total)); + sync.wakeUp(requestID, JsonUtil.toJson(req2)); + + Thread.sleep(300); + } + fin.close(); + + req.put("isDone", "true"); + req.remove("data"); + controller.sendMsg(JsonUtil.toJson(req)); + + // delete ypk_NC + LOGGER.debug("[NodeCenterActions] send project finish."); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + } + + @Action(async = true) + public void onReceiveProject(JsonObject args, final ResultCallback rc) { + LOGGER.debug("[NodeCenterActions] onReceiveProject : position----8"); + + // TODO maybe add pubkey into args. + Map req = new HashMap<>(); + req.put("operation", "onReceive"); + req.put("peerID", NodeCenterActions.getPeerIdByNodeId(args.get("nodeID").getAsString())); + req.put("progress", args.get("progress").getAsString()); + req.put("requestID", args.get("requestID").getAsString()); + String requestID = args.get("requestID").getAsString(); + sync.wakeUp(requestID, JsonUtil.toJson(req)); + } + + /* + @Action(async = true) + public void startContractInEachNode(Map args,final ResultCallback rc) { + logger.debug("[NodeCenterActions] startContractInEachNode : "); + + Map req = new HashMap<>(); + req.put("action", "startContractByYpk"); + req.put("fileName", args.get("fileName")); + req.put("contractOwner",args.get("pubKey")); + req.put("contractSig",args.get("signature")); + + String requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); + req.put("requestID", requestID); + sync.sleep(requestID, rc); + controller.sendMsg(gson.toJson(req)); + } + + + @Action(async = true) + public void onStartEachNode(Map args, final ResultCallback rc) { + logger.debug("[NodeCenterActions] onStartEachNode : "); + + logger.debug("---action" + args.get("action")); + logger.debug("---requestID" + args.get("requestID")); + logger.debug("---data" + args.get("data")); + logger.debug("---cid" + args.get("cid")); + logger.debug("---executeTime" + args.get("executeTime")); + + + String str = "onStartEachNode;" + args.get("data") + ";" + args.get("cid") + ";" + + args.get("executeTime"); + sync.wakeUp(args.get("requestID"),str); + } + */ + + @Action(async = true) + public void receiveProject(JsonObject args, final ResultCallback rc) { + LOGGER.debug("[NodeCenterActions] receiveProject : position----4"); + + String fileName = args.get("fileName").getAsString() + "_NC"; + boolean isAppend = args.get("isAppend").getAsBoolean(); + boolean isDone = args.get("isDone").getAsBoolean(); + boolean isPrivate = args.get("isPrivate").getAsBoolean(); + + LOGGER.debug("[NodeCenterActions] isAppend=" + isAppend + " isDone=" + isDone); + + String path = "./temp/"; + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + + FileOutputStream fout = null; + if (!isAppend) { + try { + fout = new FileOutputStream(new File(dir, fileName)); + fileMap.put(fileName, fout); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } else { + fout = fileMap.get(fileName); + } + + if (isDone) { + if (fout != null) { + try { + fout.close(); + fileMap.remove(fileName); + } catch (IOException e) { + e.printStackTrace(); + } + } + // TODO 参数可以加入文件名? + LOGGER.debug("[NodeCenterActions] receive finish. position----4"); + String receiveFileName = new File(dir, fileName).getAbsolutePath(); + + Map req = new HashMap<>(); + req.put("operation", "distribute"); + req.put("requestID", args.get("requestID").getAsString()); + req.put("isPrivate", isPrivate + ""); + req.put("receiveFileName", receiveFileName); + sync.wakeUp(args.get("requestID").getAsString(), JsonUtil.toJson(req)); + } else { + String data = args.get("data").getAsString(); + try { + String progress = args.get("progress").getAsString(); + byte[] encodedData = ByteUtil.decodeBASE64(data); + assert null != fout; + assert null != encodedData; + // TODO pulish progress + fout.write(encodedData); + + Map req = new HashMap<>(); + req.put("operation", "NCreceive"); + req.put("progress", progress); + req.put("requestID", args.get("requestID").getAsString()); + sync.wakeUp(args.get("requestID").getAsString(), JsonUtil.toJson(req)); + + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Action(async = true) + public void requestConnectToMaster(JsonObject args, final ResultCallback rc) { + String master = args.get("master").getAsString(); + Map key2address = new HashMap<>(); + // nodes' pubKey + JsonArray members = args.get("members").getAsJsonArray(); + Map request = new HashMap<>(); + request.put("action", "requestConnectToMaster"); + request.put("members", members); // nodes' pubKey + request.put("master", master); + if (args.has("connectAll")) + request.put("connectAll", args.get("connectAll").getAsBoolean()); + if (args.has("contractID")) { + request.put("contractID", args.get("contractID")); + } + request.put("key2address", key2address); // nodes' pubKey , master + for (int i = 0; i < members.size(); i++) { + String str = members.get(i).getAsString(); + if (nodeInfos.containsKey(str)) { + CMNode node = nodeInfos.get(str); + key2address.put(str, node.masterAddress); + } + } + String req = JsonUtil.toJson(request); + for (int i = 0; i < members.size(); i++) { + String str = members.get(i).getAsString(); + if (nodeInfos.containsKey(str)) { + CMNode node = nodeInfos.get(str); + node.connection.controller.sendMsg(req); + } + } + } + + // static boolean clearCache() { + // + // if (requestCache.isEmpty()) return true; + // + // final long time = System.currentTimeMillis() - 120000L; // 120s + // requestCache + // .entrySet() + // .removeIf( + // entry -> { + // RequestCache cache = entry.getValue(); + // if (cache == null) return true; + // return cache.getTime() < time; + // }); + // + // return false; + // } + + // static RequestCache getCache(String contractID) { + // if (contractID != null) { + // RequestCache cache = requestCache.get(contractID); + // if (cache != null) return cache; + // else { + // logger.debug("[NodeCenterActions] create requestcache:" + contractID); + // RequestCache reqc = new RequestCache(); + // requestCache.put(contractID, reqc); + // return reqc; + // } + // } + // return null; + // } + + /* @Action(async = true) + public void sendCachedRequests(Map args, final ResultCallback rc) { + String contractID = args.get("contractID"); + int start = Integer.parseInt(args.get("start")); + int end = Integer.parseInt(args.get("end")); + + RequestCache cache = getCache(contractID); + for (int i = start + 1; i < end; i++) { + if (cache == null || !cache.containsKey(i)) { + + // this node crash + String nodeID = args.get("nodeID"); + restartContracts(nodeID); + } + JsonObject jo = cache.get(i); + controller.sendMsg(JsonUtil.toJson(jo)); + + logger.debug("NC发送第 " + jo.get("seq").getAsString() + " 个请求给Node"); + } + }*/ + + /* @Action(async = true) + public void recoverFinish(Map args, final ResultCallback rc) { + ContractRecord cr = recoverMap.get(args.get("nodeID")).get(args.get("contractID")); + if (cr.recoverFlag == RecoverFlag.ToRecover) cr.recoverFlag = RecoverFlag.Fine; + }*/ + + @Action(async = true) + public void electMaster(JsonObject args, final ResultCallback rc) { + String contractID = args.get("contractID").getAsString(); + int lastExe = args.get("lastExe").getAsInt(); + String nodeID = args.get("nodeID").getAsString(); + + LOGGER.info("electMaster nodeID=" + nodeID); + + ElectMasterTimeRecorder.nodeFindMasterCrash.put(nodeID, System.currentTimeMillis()); + + String masterID = args.get("master").getAsString(); // 旧的master + String members = args.get("members").getAsString(); // 执行这个合约所有节点的pubkey + String uniNumber = null; + if (args.has("uniNumber")) uniNumber = args.get("uniNumber").getAsString(); + + // if (nodeinfos.containsKey(masterID)) { + // CMNode node = nodeinfos.get(masterID); + // synchronized (node) { + // for (ContractDesp cd : node.contracts) { + // if (cd.contractID.equals(contractID) || + // cd.contractName.equals(contractID)) { + // cd.setIsMaster(false); + // LOGGER.debug( + // "选举步骤1---- 设置节点 " + // + node.pubKey.substring(0, 5) + // + " 的合约 " + // + contractID + // + " isMaster=" + // + false); + // } + // } + // } + // } + + synchronized (NCElectMasterUtil.electInfos) { + if (!NCElectMasterUtil.electInfos.containsKey(contractID)) { + NCElectMasterUtil.electInfos.put( + contractID, + new NCElectMasterUtil.ElectInfo(masterID, contractID, members, uniNumber)); + } + NCElectMasterUtil.ElectInfo eleInfo = NCElectMasterUtil.electInfos.get(contractID); + eleInfo.put(nodeID, lastExe, masterID, members, uniNumber); + } + } + + @Action(async = true) + public void queryAllRouteInfo(JsonObject args, final ResultCallback rc) { + args.addProperty("action", "onQueryAllRouteInfo"); + JsonArray array = new JsonArray(); + // JsonObject + for (Map.Entry stringCMNodeEntry : nodeInfos.entrySet()) { + CMNode node = stringCMNodeEntry.getValue(); + if (node.contracts == null) continue; + for (ContractDesp desp : node.contracts) { + if (desp.type == ContractExecType.Sole || desp.getIsMaster()) { + array.add(extractContractRoute(node, desp)); + } + } + } + args.add("result", array); + rc.onResult(args.toString()); + } + + private JsonObject extractContractRoute(CMNode node, ContractDesp desp) { + JsonObject info = new JsonObject(); + info.addProperty("pubKey", node.pubKey); + info.addProperty("masterAddress", node.masterAddress); + info.addProperty("contractID", desp.contractID); + info.addProperty("contractName", desp.contractName); + return info; + } + + @Action(async = true) + public void queryRouteInfo(JsonObject args, final ResultCallback rc) { + String contractID = args.get("contractID").getAsString(); + args.addProperty("action", "onQueryRouteInfo"); + args.add("responseID", args.get("requestID")); + args.add("result", JsonNull.INSTANCE); + if (contractID == null || contractID.length() == 0) { + args.add("result", new JsonPrimitive("Not Found")); + } + for (Map.Entry stringCMNodeEntry : nodeInfos.entrySet()) { + CMNode node = stringCMNodeEntry.getValue(); + if (node.contracts == null) continue; + for (ContractDesp desp : node.contracts) { + if (contractID.equals(desp.contractID) || contractID.equals(desp.contractName)) { + LOGGER.info( + "查看合约 " + + desp.contractID + + " " + + desp.contractName + + " 节点 " + + node.pubKey.substring(0, 5) + + " " + + desp.type + + " " + + desp.getIsMaster()); + if (desp.type == ContractExecType.Sole) { + args.add("result", extractContractRoute(node, desp)); + } else if (desp.getIsMaster()) { + LOGGER.info( + "合约 " + contractID + " 的master为 " + node.pubKey.substring(0, 5)); + args.add("result", extractContractRoute(node, desp)); + } + } + } + } + + /* logger.debug("查看合约 " + contractID + " 的master为 " + args.get("result")); + if(!args.containsKey("result") || args.get("result") == null || args.get("result").equals("null")){ + //NC发起各个节点重选 + logger.debug("NC发现合约 " + contractID + " 的master为null,NC向该合约的各个节点发起重选请求!"); + Map req = new HashMap<>(); + req.put("action","NCStartElect"); + req.put("contractID",contractID); + for(CMNode node : nodeinfos.values()){ + node.connection.controller.sendMsg(JsonUtil.toJson(req)); + break; + } + }*/ + + if (args.get("result").equals("null")) { + LOGGER.info("[NodeCenterActions] query route by info from other NC"); + + // 查询其他节点的信息 + String s = OtherNCProxy.instance.search(contractID); + LOGGER.info( + "[NodeCenterActions] query route by info from other NC " + + (s == null ? "null" : s)); + if (s != null) { + String[] ss = s.split(";"); + args.addProperty( + "result", + "{\"pubKey\":\"" + ss[0] + "\",\"masterAddress\":\"" + ss[1] + "\"}"); + } + } + + rc.onResult(JsonUtil.toJson(args)); + } + + @Action(async = true) + public void NCStartElect(JsonObject args, final ResultCallback rc) { + String contractID = args.get("contractID").getAsString(); + + LOGGER.debug("查看合约 " + contractID + " 的master为 " + args.get("result")); + if (!args.has("result") + || args.get("result") == null + || args.get("result").getAsString().equals("null")) { + // NC发起各个节点重选 + LOGGER.debug("NC发现合约 " + contractID + " 的master为null,NC向该合约的各个节点发起重选请求!"); + Map req = new HashMap<>(); + req.put("action", "NCStartElect"); + req.put("contractID", contractID); + req.put("uniNumber", System.currentTimeMillis() + "_" + new Random().nextLong()); + for (CMNode node : nodeInfos.values()) { + node.connection.controller.sendMsg(JsonUtil.toJson(req)); + LOGGER.info( + "[ADSP] [NodeCenterActions] 发现合约 " + + contractID + + " 的master为null 向节点 " + + node.nodeName + + "发送选举请求"); + } + } + } + + // 另可能仅和旧的master断开和NC连接的节点连新的master + // @Action(async = true) + // public void updateFormerMaster(JsonObject args, final ResultCallback rc) { + // String[] offlines = args.get("offlines").getAsString().split(","); + // String contractID = args.get("contractID").getAsString(); + // String master = args.get("master").getAsString(); + // + // for (String nodeID : offlines) { + // if (nodeinfos.containsKey(nodeID)) { + // CMNode node = nodeinfos.get(nodeID); + // for (ContractDesp desp : node.contracts) { + // if (desp.contractID.equals(contractID) + // || desp.contractName.equals(contractID)) { + // Map request = new HashMap<>(); + // request.put("action", "setClientNotMaster"); + // request.put("contractID", contractID); + // node.connection.controller.sendMsg(JsonUtil.toJson(request)); + // request.put("action", "queryUnitContractsID"); + // node.connection.controller.sendMsg(JsonUtil.toJson(request)); + // } + // } + // } + // } + // } + + @Action(async = true) + public void changeMaster(JsonObject args, final ResultCallback rc) { + String contractID = args.get("contractID").getAsString(); + String master = args.get("master").getAsString(); + LOGGER.debug("设置合约 " + contractID + " 的master为 " + master.substring(0, 5)); + CMNode node = nodeInfos.get(master); + synchronized (node) { + for (ContractDesp cd : node.contracts) { + if (cd.contractID.equals(contractID) || cd.contractName.equals(contractID)) { + cd.setIsMaster(true); + LOGGER.debug( + "设置节点 " + + node.pubKey.substring(0, 5) + + " 的合约 " + + cd.contractID + + " " + + cd.contractName + + " isMaster=" + + cd.getIsMaster()); + } + } + } + + ElectMasterTimeRecorder.changeMasterFinish = System.currentTimeMillis(); + } + + @Action(async = true, userPermission = 0) + public void distributeContract(JsonObject args, final ResultCallback rc) { + LOGGER.debug("[UnitActions] distributeContract : -----------position1"); + + String signature = args.get("signature").getAsString(); + String projectName = args.get("projectName").getAsString(); + boolean isPrivate = args.get("isPrivate").getAsBoolean(); + String pubKey = args.get("pubkey").getAsString(); // 用户的 + LOGGER.debug("pubkey=" + pubKey); + // String sponsorPubKey = args.get("sponsorPubKey").getAsString(); + String sponsorPubkey = args.get("sponsorPubkey").getAsString(); + LOGGER.debug("sponsorPubkey=" + sponsorPubkey); + String[] strs = + args.get("nodeIDs").getAsString().split(","); // all nodes' pubKey in the unit + Set nodePubKeys = new HashSet<>(); // nodes' pubkey + for (String str : strs) { + if (str != null && str.length() > 0) nodePubKeys.add(str); + } + Map nodes = new HashMap<>(); // node's pubKey-node'a name + for (CMNode node : NodeCenterActions.nodeInfos.values()) { + if (nodePubKeys.contains(node.pubKey)) { + nodes.put(node.pubKey, node.nodeName); + LOGGER.debug("nodes add " + node.pubKey + " " + node.nodeName); + } + } + + String distributeID = args.get("distributeID").getAsString(); + LOGGER.debug("distributeID=" + distributeID); + DistributeCallback dc = + new DistributeCallback(distributeID, sponsorPubkey, nodes, rc, pubKey, signature); + + LOGGER.debug("[UnitActions] signature=" + signature); + LOGGER.debug("[UnitActions] pubKey=" + pubKey); + + // 身份验证 + + boolean result = + SM2Util.plainStrVerify( + pubKey, "DistributeContract|" + projectName + "|" + pubKey, signature); + + LOGGER.debug("[UnitAcitons] 验证:" + result + " -> projectName:" + projectName); + + // requestProject + if (result) { + for (CMNode node : NodeCenterActions.nodeInfos.values()) { + LOGGER.debug(node.nodeName + " " + node.pubKey); + } + CMNode sponsor = NodeCenterActions.nodeInfos.get(sponsorPubkey); + LOGGER.debug("[UnitActions] sponsor=" + sponsor.pubKey); + + sponsor.connection.requestProject(projectName, pubKey, isPrivate, dc); + } else { + rc.onResult( + "{\"status\":\"Identity error\"" + ",\"action\":\"onStartTrustfulContract\"}"); + } + } + + @Action + public void requestRouteInfo(JsonObject args, final ResultCallback rc) { + Map to = new HashMap<>(); + List info = OtherNCProxy.instance.getInfo(); + if (info.isEmpty()) { + LOGGER.info("[NodeCenterActions] requestRouteInfo info is empty"); + to.put("empty", "true"); + } + final String centerManger = "__CenterManager__"; + String id = KeyValueDBUtil.instance.getValue(NCTables.ConfigDB.toString(), centerManger); + to.put("action", "receiveUpdate"); + to.put("address", args.get("address").getAsString()); + to.put("id", id); + to.put("contracts", JsonUtil.toJson(info)); + LOGGER.info(JsonUtil.toJson(info)); + + controller.sendMsg(JsonUtil.toJson(to)); + } + + @Action(async = true) + public void askElectMasterTimeRecorder(JsonObject args, ResultCallback resultCallback) { + String data = "null"; + String item = args.get("item").getAsString(); + if (args.has("slave")) { + String nodeID = args.get("nodeID").getAsString(); + if ("nodeFindMasterCrash".equals(item)) { + if (ElectMasterTimeRecorder.nodeFindMasterCrash.containsKey(nodeID)) { + data = ElectMasterTimeRecorder.nodeFindMasterCrash.get(nodeID) + ""; + } + } else { + data = "null"; + } + } else { + switch (item) { + case "startElect": + if (ElectMasterTimeRecorder.startElect != null) { + data = ElectMasterTimeRecorder.startElect + ""; + } + break; + case "findNewMaster": + if (ElectMasterTimeRecorder.findNewMaster != null) { + data = ElectMasterTimeRecorder.findNewMaster + ""; + } + break; + case "newMaster": + if (ElectMasterTimeRecorder.newMaster != null) { + data = ElectMasterTimeRecorder.newMaster + ""; + } + break; + case "changeMasterFinish": + if (ElectMasterTimeRecorder.changeMasterFinish != null) { + data = ElectMasterTimeRecorder.changeMasterFinish + ""; + } + break; + default: + data = "null"; + break; + } + } + + JsonObject ret = new JsonObject(); + ret.addProperty("action", "onAskElectMasterTimeRecorder"); + ret.addProperty("data", data); + resultCallback.onResult(ret.toString()); + } + + @Action(userPermission = 0, async = true) + public void queryAEState(JsonObject json, ResultCallback resultCallback) { + JsonObject ret = new JsonObject(); + ret.addProperty("action", "onQueryAEState"); + if (json.has("caller")) { + ret.add( + "result", + JsonParser.parseString( + JsonUtil.toJson( + ActionExecutor.getStatistic( + json.get("caller").getAsString())))); + } else { + ret.add("result", JsonParser.parseString(JsonUtil.toJson(ActionExecutor.getAllData()))); + } + resultCallback.onResult(ret.toString()); + } + + @Action(userPermission = 0, async = true) + public void queryAECaller(JsonObject json, ResultCallback resultCallback) { + JsonObject ret = new JsonObject(); + ret.addProperty("action", "onQueryAECaller"); + ret.add("result", JsonParser.parseString(JsonUtil.toJson(ActionExecutor.getAECallerSet()))); + resultCallback.onResult(ret.toString()); + } + + public static class ResultCollector extends ResultCallback { + private final ResultCallback committer; + List result = new ArrayList<>(); + String requestID; + int repeatCount; + + public ResultCollector(String reqID, ResultCallback committer, int count) { + this.committer = committer; + repeatCount = count; + requestID = reqID; + } + + // 这个最好是把commit的结果也放过去?不要集齐全部再返回,而是一项项返回? + // 需要在服务端记录"不正常节点?" + @Override + public void onResult(String str) { + result.add(str); + repeatCount--; + if (repeatCount > 0) { + sync.sleepWithTimeout(requestID, this, 20); + } else { + committer.onResult(JsonUtil.toJson(result)); + } + } + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NodeCenterFrameHandler.java b/src/main/java/org/bdware/server/nodecenter/NodeCenterFrameHandler.java new file mode 100644 index 0000000..af9aae5 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NodeCenterFrameHandler.java @@ -0,0 +1,168 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.bdware.server.action.Action; +import org.bdware.server.action.ActionExecutor; + +import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NodeCenterFrameHandler extends SimpleChannelInboundHandler { + private static final Logger LOGGER = LogManager.getLogger(LogActions.class); + static ExecutorService executorService = Executors.newFixedThreadPool(10); + public String pubKey; + NodeCenterActions actions; + MasterActions masterActions; + ActionExecutor ae; + ChannelHandlerContext ctx; + + public NodeCenterFrameHandler() { + actions = new NodeCenterActions(this); + MetaIndexAction.controller = this; + // TODO 添加那个UnitAction. + ae = + new ActionExecutor( + executorService, actions, new MetaIndexAction()) { + @Override + public boolean checkPermission( + Action a, final JsonObject args, long permission) { + long val = a.userPermission(); + boolean flag; + String status = "refuse"; + String action = args.get("action").getAsString(); + if (val == 0) { + flag = true; + status = "accept"; + } else if ((permission & val) == val) { + flag = true; + status = "accept"; + } else { + flag = false; + } + if (!action.contains("checkAlive")) + NodeCenterServer.nodeTcpLogDB.put( + action, + "{\"action\":\"" + + action + + "\",\"pubKey\":\"" + + pubKey + + "\",\"status\":\"" + + status + + "\",\"date\":" + + System.currentTimeMillis() + + "}"); + return flag; + } + }; + LOGGER.info("create NodeCenterFrameHandler instance succeed!"); + } + + public void setPermission(long l) { + ae.permission = l; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Object frame) { + this.ctx = ctx; + JsonObject map; + try { + map = + JsonParser.parseReader( + new InputStreamReader(new ByteBufInputStream((ByteBuf) frame))) + .getAsJsonObject(); + + } catch (Exception e) { + LOGGER.error("can't handle msg! " + e.getMessage()); + return; + } + Response response; + try { + final String action = map.get("action").getAsString(); + // System.out.println("[NodeCenterFramHandler] handle:" + action); + ae.handle( + action, + map, + new ResultCallback() { + @Override + public void onResult(String ret) { + sendMsg(ret); + } + }); + } catch (IllegalArgumentException e) { + response = new Response(); + response.action = "onException"; + response.data = e.getMessage(); + LOGGER.error(JsonUtil.toJson(response)); + } catch (Exception e) { + e.printStackTrace(); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + response = new Response(); + response.action = "onException"; + String[] strs = bo.toString().split("\n"); + StringBuilder ret = new StringBuilder(); + int count = 0; + for (String s : strs) { + if (s.contains("sun.reflect") + || s.contains("java.lang.reflect") + || s.contains("org.apache") + || s.contains("java.util") + || s.contains("java.lang") + || s.contains("io.netty")) { + continue; + } + ret.append(s); + ret.append("\n"); + if (count++ > 10) { + break; + } + } + response.data = ret.toString(); + LOGGER.error(JsonUtil.toJson(response)); + } + } + + public void sendMsg(String json) { + if (ctx != null) { + // System.out.println("[NodeCenterFrame send] TID:" + Thread.currentThread().getId() + " + // isOpen:" + // + ctx.channel().isOpen() + " isActive:" + ctx.channel().isActive() + " -->" + json); + ByteBuf buf = Unpooled.wrappedBuffer(json.getBytes()); + ctx.channel().writeAndFlush(buf); + } + } + + public void sendEMsg(String msg) { + Response r = new Response(); + r.action = "receiveEMsg"; + r.data = msg; + sendMsg(JsonUtil.toJson(r)); + } + + public boolean isOpen() { + return ctx.channel().isOpen(); + } + + static class Response { + public String cid; + public String analysisResult; + String responseID; + String action; + Object data; + long executeTime; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/NodeCenterWSFrameHandler.java b/src/main/java/org/bdware/server/nodecenter/NodeCenterWSFrameHandler.java new file mode 100644 index 0000000..827c656 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/NodeCenterWSFrameHandler.java @@ -0,0 +1,171 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketFrame; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.bdware.server.action.Action; +import org.bdware.server.action.ActionExecutor; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class NodeCenterWSFrameHandler extends SimpleChannelInboundHandler { + public static ExecutorService executorService = Executors.newFixedThreadPool(10); + private static final Logger LOGGER = LogManager.getLogger(NodeCenterWSFrameHandler.class); + StringBuilder dataCache = new StringBuilder(); + ActionExecutor ae; + private ChannelHandlerContext ctx; + private NCManagerAction managerAction; + private LogActions logActions; + private UnitActions unitActions; + + public NodeCenterWSFrameHandler() { + managerAction = new NCManagerAction(this); + logActions = new LogActions(managerAction); + unitActions = new UnitActions(managerAction); + + + ae = + new ActionExecutor( + executorService, managerAction, logActions, unitActions, new MetaIndexAction(), new TracingAction()) { + @Override + public boolean checkPermission(Action a, JsonObject arg, long permission) { + // Permission userPermission = a.userPermission(); + // long val=userPermission.getValue(); + long val = a.userPermission(); + String pubKey = managerAction.pubKey; + String action = arg.get("action").getAsString(); + LOGGER.debug("permission" + permission); + LOGGER.debug("userPermission" + val); + NodeCenterServer.nodeHttpLogDB.put( + action, + "{\"action\":\"" + + action + + "\",\"pubKey\":\"" + + pubKey + + "\",\"date\":" + + System.currentTimeMillis() + + "}"); + + // if (val == 0) return true; + // return true; + if ((permission & val) == val) { + return true; + } else return false; + } + }; + } + + public ActionExecutor getAE() { + return ae; + } + + @Override + protected void channelRead0(final ChannelHandlerContext ctx, WebSocketFrame frame) + throws Exception { + // ping and pong frames already handled + if (frame instanceof TextWebSocketFrame) { + // Send the uppercase string back. + this.ctx = ctx; + String request = ((TextWebSocketFrame) frame).text(); + LOGGER.debug("[Websocket receive]" + request); + JsonObject map = null; + try { + map = new JsonParser().parse(request).getAsJsonObject(); + } catch (Exception e) { + Response response = new Response(); + response.action = "onException"; + response.data = "msg is not JSON format"; + ctx.channel().writeAndFlush(new TextWebSocketFrame(JsonUtil.toJson(response))); + return; + } + Response response = new Response(); + response.action = "sendNextSegment"; + try { + boolean isSegment = false; + if (map.has("isSegment")) isSegment = map.get("isSegment").getAsBoolean(); + if (isSegment) { + dataCache.append(map.get("data").getAsString()); + response = new Response(); + response.action = "sendNextSegment"; + ctx.channel() + .writeAndFlush(new TextWebSocketFrame(JsonUtil.toJson(response))); + return; + } else { + if (dataCache.length() > 0) { + dataCache.append(map.get("data").getAsString()); + String dataStr = dataCache.toString(); + LOGGER.debug("[WebSocketFrame] DataString:" + dataStr); + map = new JsonParser().parse(dataStr).getAsJsonObject(); + dataCache = new StringBuilder(); + } + } + String action = map.get("action").getAsString(); + ae.handle( + action, + map, + new ResultCallback() { + @Override + public void onResult(String ret) { + if (ret != null) + ctx.channel().writeAndFlush(new TextWebSocketFrame(ret)); + } + }); + } catch (IllegalArgumentException e) { + response = new Response(); + response.action = "onException"; + response.data = e.getMessage(); + ctx.channel().writeAndFlush(new TextWebSocketFrame((JsonUtil.toJson(response)))); + return; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(); + e.printStackTrace(new PrintStream(bo)); + response = new Response(); + response.action = "onException"; + String[] strs = bo.toString().split("\n"); + String ret = ""; + int count = 0; + for (String s : strs) { + if (s.contains("sun.reflect")) continue; + if (s.contains("java.lang.reflect")) continue; + if (s.contains("org.apache")) continue; + if (s.contains("java.util")) continue; + if (s.contains("java.lang")) continue; + if (s.contains("io.netty")) continue; + ret += s; + ret += "\n"; + if (count++ > 5) break; + } + response.data = ret; + ctx.channel().writeAndFlush(new TextWebSocketFrame(JsonUtil.toJson(response))); + } + + } else { + Response response = new Response(); + response.action = "onException"; + response.data = "unsupported frame"; + ctx.channel().writeAndFlush(new TextWebSocketFrame((JsonUtil.toJson(response)))); + return; + } + } + + public void sendMsg(String json) { + LOGGER.debug("[WebSocketFrameHandler] sendMsg:" + json); + ctx.channel().writeAndFlush(new TextWebSocketFrame(json)); + } + + public void setPermission(long i) { + ae.permission = i; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/OtherNCProxy.java b/src/main/java/org/bdware/server/nodecenter/OtherNCProxy.java new file mode 100644 index 0000000..2ca3913 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/OtherNCProxy.java @@ -0,0 +1,287 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.bean.ContractExecType; +import org.bdware.sc.bean.OtherNCInfo; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.NodeCenterServer; +import org.bdware.server.ws.DelimiterCodec; +import org.zz.gmhelper.SM2KeyPair; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public class OtherNCProxy { + private static final Logger LOGGER = LogManager.getLogger(OtherNCProxy.class); + public static OtherNCProxy instance = new OtherNCProxy(); + private final TreeMap> otherNodeCenterInfos = new TreeMap<>(); //key为NC节点的pubkey(非一致的) + public Map connList = new ConcurrentHashMap<>(); //连接别的NC key为ip:port + public Set ncAdds = ConcurrentHashMap.newKeySet(); //其他NC的ip:port + public SM2KeyPair sm2; //NC证书 + + private OtherNCProxy() { + } + + // 从DB中加载OtherNC和NCFile的信息 + public void init() { + LOGGER.debug("[OtherNCProxy] init : "); + + loadOtherNCs(); + LOGGER.debug("load other ncs"); + + loadNCFile(); + LOGGER.debug("load nc file"); + + periodUpdate(); + LOGGER.debug("period update"); + + LOGGER.info("OtherNCProxy init done!"); + } + + public void periodUpdate() { + NodeCenterServer.scheduledThreadPool.scheduleWithFixedDelay( + () -> { + }, + 0, + 30, + TimeUnit.SECONDS); + } + + public void loadOtherNCs() { + LOGGER.debug("loadOtherNCs : "); + + String res = getOtherNCs(); + if (res != null && !res.equals("")) { + String[] strArr = res.split(";"); + ncAdds.clear(); + ncAdds.addAll(Arrays.asList(strArr)); + + LOGGER.debug("now ncAdds contains: "); + for (String s : strArr) { + LOGGER.debug(s); + } + + if (ncAdds.isEmpty()) { + KeyValueDBUtil.instance.setValue(NCTables.OtherNCs.toString(), "address", ""); + } + } else { + ncAdds.clear(); + } + } + + // 配置一致的NC证书 + public void loadNCFile() { + LOGGER.debug("loadNCFile"); + + String fileAdd = getNCFile(); + if (fileAdd != null && !fileAdd.isEmpty()) { + LOGGER.debug("NCFile path = " + fileAdd); + + File file = new File(fileAdd); + if (!file.exists()) { + LOGGER.warn("file not exists!"); + KeyValueDBUtil.instance.setValue(NCTables.NCFile.toString(), "filePath", ""); + return; + } + + String keyPairStr = null; + try { + FileReader fr = new FileReader(file); + BufferedReader br = new BufferedReader(fr); + String s; + while (null != (s = br.readLine())) { + keyPairStr = s; + } + fr.close(); + br.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (keyPairStr == null) { + LOGGER.warn("import ket pair form NCFile is null!"); + return; + } + LOGGER.info("import ket pair from NCFile : " + keyPairStr); + sm2 = SM2KeyPair.fromJson(keyPairStr); + } else { + sm2 = null; + } + } + + public String getOtherNCs() { + return KeyValueDBUtil.instance.getValue(NCTables.OtherNCs.toString(), "address"); + } + + public String getNCFile() { + return KeyValueDBUtil.instance.getValue(NCTables.NCFile.toString(), "filePath"); + } + + //配置其他NC + public String setOtherNCs(String add) { + LOGGER.debug("setOtherNC: " + add); + + KeyValueDBUtil.instance.setValue(NCTables.OtherNCs.toString(), "address", add); + loadOtherNCs(); + return getOtherNCs(); + } + + //配置NC文件 + public String setNCFile(String fileName) { + LOGGER.debug("setNCFIle: " + fileName); + + KeyValueDBUtil.instance.setValue(NCTables.NCFile.toString(), "filePath", fileName); + loadNCFile(); + return getNCFile(); + } + + //本地更新 + public void updateOtherNCInfo(String nodePubkey, List list) { + LOGGER.debug("updateOtherNCInfo: "); + + synchronized (otherNodeCenterInfos) { + otherNodeCenterInfos.put(nodePubkey, list); + LOGGER.debug("update route nodePubkey=" + nodePubkey + " list=" + JsonUtil.toJson(list)); + } + } + + //通过合约id或name在别的nc上查询信息 + public String search(String id) { + LOGGER.debug("search : " + id); + synchronized (otherNodeCenterInfos) { + for (String pubKey : otherNodeCenterInfos.keySet()) { + List list = otherNodeCenterInfos.get(pubKey); + for (OtherNCInfo info : list) { + if (info.contractID.equals(id) || info.contractName.equals(id)) { + LOGGER.debug("[OtherNCProxy] find contract " + id + "pubKey is " + pubKey + " master is " + info.master + " from other NC route info"); + return pubKey + ";" + info.master; + } + } + } + return null; + } + } + + //向其他NC发起同步 + public void requestUpdate() { + LOGGER.debug("sendUpdate: "); + for (String add : instance.ncAdds) { + try { + instance.connectToNC(add); + } catch (Exception e) { + LOGGER.error("NC " + add + " can not be connected! " + e.getMessage()); + connList.get(add).close(); + continue; + } + JsonObject args = new JsonObject(); + args.addProperty("action", "requestRouteInfo"); + args.addProperty("address", add); + connList.get(add).sendMsg(JsonUtil.toJson(args)); + } + } + + public List getInfo() { + LOGGER.debug("getInfo: "); + + List list = new ArrayList<>(); + for (Map.Entry stringCMNodeEntry : NodeCenterActions.nodeInfos.entrySet()) { + CMNode node = stringCMNodeEntry.getValue(); + if (null == node.contracts) + continue; + for (ContractDesp desp : node.contracts) { + if (desp.type == ContractExecType.Sole || desp.getIsMaster()) { + list.add(new OtherNCInfo(desp.contractID, desp.contractName, node.masterAddress)); + } + } + } + return list; + } + + public void connectToNC(String target) throws InterruptedException { + LOGGER.debug("connectToNC: "); + if (target == null || !target.contains(":")) { + LOGGER.error("connectToNC failed, target nc address is illegal! target=" + target); + return; + } + + final Bootstrap b = new Bootstrap(); + b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000); + EventLoopGroup group = new NioEventLoopGroup(); + b.group(group); + NCConnector conn = new NCConnector(target, b, new NCClientHandler()); + connList.put(target, conn); + b.channel(NioSocketChannel.class) + .handler( + new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + ChannelPipeline p = ch.pipeline(); + p.addLast(new DelimiterCodec()).addLast(conn.handler); + } + }); + + + if (!conn.handler.isOpen()) { + String[] ipAndPort = target.split(":"); + b.connect(ipAndPort[0], Integer.parseInt(ipAndPort[1])) + .sync() + .channel(); + } + + reconnect(target); + } + + public void reconnect(String target) throws InterruptedException { + LOGGER.debug("reconnect: "); + + if (!connList.containsKey(target)) + return; + + NCConnector conn = connList.get(target); + if (!conn.handler.isOpen()) { + String[] ipAndPort = conn.ipAndPort.split(":"); + conn.bootstrap + .connect(ipAndPort[0], Integer.parseInt(ipAndPort[1])) + .sync() + .channel(); + } + } + + static class NCConnector { + Bootstrap bootstrap; + NCClientHandler handler; + String ipAndPort; + + public NCConnector(String s, Bootstrap b, NCClientHandler h) { + bootstrap = b; + handler = h; + ipAndPort = s; + } + + public void sendMsg(String s) { + handler.sendMsg(s); + } + + public void close() { + handler.close(); + OtherNCProxy.instance.connList.remove(ipAndPort); + } + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/TracingAction.java b/src/main/java/org/bdware/server/nodecenter/TracingAction.java new file mode 100644 index 0000000..f926228 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/TracingAction.java @@ -0,0 +1,61 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import com.google.gson.JsonArray; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.server.action.Action; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class TracingAction { + private static JsonObject getDependentContract(JsonObject jo) { + //考虑多层依赖 + JsonObject result = new JsonObject(); + JsonArray beDependent = new JsonArray(); + String contractName = jo.get("contractName").getAsString(); + try { + if (!jo.has("contractName")) { + result.addProperty("exception", "missing arguments: contractName"); + return result; + } + JsonArray dependencies = new JsonArray(); + for (CMNode node : NodeCenterActions.nodeInfos.values()) { + for (ContractDesp desp : node.contracts) { + if (desp.contractName.equals(contractName)) { + if (desp.dependentContracts != null) { + for (String val : desp.dependentContracts) + dependencies.add(val); + } + } else { + if (desp.dependentContracts != null && desp.dependentContracts.contains(contractName)) { + beDependent.add(desp.contractName); + } + } + } + } + result.add("dependencies", dependencies); + result.add("beDependent", beDependent); + + return result; + } catch (Exception e) { + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(bo)); + result.addProperty("exception", bo.toString()); + + return result; + } + } + + @Action(async = true) + public void getDependentContract(JsonObject json, ResultCallback rc) { + JsonObject result = getDependentContract(json); + JsonObject jo = new JsonObject(); + if (json.has("requestID")) + jo.add("responseID", json.get("requestID")); + jo.add("result", result); + jo.addProperty("action", "onGetDependentContract"); + rc.onResult(jo.toString()); + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/UnitActions.java b/src/main/java/org/bdware/server/nodecenter/UnitActions.java new file mode 100644 index 0000000..3c5c6d9 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/UnitActions.java @@ -0,0 +1,407 @@ +package org.bdware.server.nodecenter; + +import com.google.gson.JsonObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.sc.Jedion.KV; +import org.bdware.sc.bean.ContractDesp; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.sc.db.KeyValueDBUtil; +import org.bdware.sc.util.JsonUtil; +import org.bdware.server.action.Action; + +import java.util.*; + +public class UnitActions { + private static final Logger LOGGER = LogManager.getLogger(UnitActions.class); + private final NCManagerAction managerAction; + + public UnitActions(NCManagerAction managerAction) { + this.managerAction = managerAction; + } + + private static String convertContractName(String contractID) { + ContractDesp desp = NodeCenterActions.getContractByName(contractID); + if (desp != null) return desp.contractID; + return contractID; + } + + /** + * 显示当前用户可见的集群 + * + * @param json + * @param resultCallback + */ + @Action(userPermission = 1 << 6, async = true) + public void listTrustUnits(JsonObject json, ResultCallback resultCallback) { + long start = System.currentTimeMillis(); + final String pubKey = managerAction.pubKey; + Map ret = new HashMap<>(); + List allunits = KeyValueDBUtil.instance.getKeyValues(NCTables.TrustUnitsDB.toString()); + + if (pubKey != null + && KeyValueDBUtil.instance + .getValue(NCTables.ConfigDB.toString(), "__CenterManager__") + .contains(pubKey)) { + LOGGER.debug("is center manager"); + ret.put("action", "onListTrustUnits"); + ret.put("data", allunits); + LOGGER.debug("[listTrustUnits] " + JsonUtil.toJson(ret)); + resultCallback.onResult(JsonUtil.toJson(ret)); + long end = System.currentTimeMillis(); + LOGGER.debug("[listTrustUnits:time]" + (end - start)); + return; + } + // 合约管理者获取自己创建的units + Iterator it = allunits.iterator(); + List ciunits = new ArrayList<>(); + while (it.hasNext()) { + KV kv = it.next(); + if (kv.key.contains(pubKey)) { + ciunits.add(kv); + } + } + ret.put("action", "onListTrustUnits"); + ret.put("data", ciunits); + LOGGER.debug("[listTrustUnits] " + JsonUtil.toJson(ret)); + resultCallback.onResult(JsonUtil.toJson(ret)); + long end = System.currentTimeMillis(); + LOGGER.debug("[listTrustUnits:time]" + (end - start)); + } + + @Action(userPermission = 1 << 6, async = true) + public void createTrustUnit(JsonObject json, ResultCallback resultCallback) { + final String pubKey = managerAction.pubKey; + LOGGER.debug("[createTrustUnit] " + json.get("data").toString()); + KeyValueDBUtil.instance.setValue( + NCTables.TrustUnitsDB.toString(), + pubKey + "_" + json.get("msg").getAsString(), + json.get("data").toString()); + Map ret = new HashMap<>(); + ret.put("action", "onCreateTrustUnit"); + ret.put("status", "Success"); + resultCallback.onResult(JsonUtil.toJson(ret)); + } + + @Action(userPermission = 1 << 6, async = true) + public void deleteTrustUnit(JsonObject json, ResultCallback resultCallback) { + // final String pubKey = managerAction.pubKey; + LOGGER.debug( + "[deleteTrustUnit] before" + + KeyValueDBUtil.instance.getValue( + NCTables.TrustUnitsDB.toString(), json.get("data").getAsString())); + KeyValueDBUtil.instance.delete( + NCTables.TrustUnitsDB.toString(), json.get("data").getAsString()); + LOGGER.debug( + "[deleteTrustUnit] after" + + KeyValueDBUtil.instance.getValue( + NCTables.TrustUnitsDB.toString(), json.get("data").getAsString())); + Map ret = new HashMap<>(); + ret.put("action", "onDeleteTrustUnit"); + ret.put("status", "Success"); + resultCallback.onResult(JsonUtil.toJson(ret)); + } + +// @Action(async = true) +// public void executeContract(JsonObject args, final ResultCallback rc) { +// executeContractTrustfully(args, rc); +// } + + @Action(async = true, userPermission = 1L << 6) + public void getNodeTrustUnits(JsonObject json, ResultCallback resultCallback) { + String nodeID = json.get("data").getAsString(); + List allunits = KeyValueDBUtil.instance.getKeyValues(NCTables.TrustUnitsDB.toString()); + Iterator it = allunits.iterator(); + List nodeunits = new ArrayList(); + while (it.hasNext()) { + KV kv = it.next(); + if (kv.value.contains(nodeID)) { + nodeunits.add(kv); + } + } + Map ret = new HashMap<>(); + ret.put("action", "onGetNodeTrustUnits"); + ret.put("data", nodeunits); + resultCallback.onResult(JsonUtil.toJson(ret)); + } + +// @Action(async = true, userPermission = 0) +// public void executeContractTrustfully(JsonObject args, final ResultCallback rc) { +// String contractID = args.get("contractID").getAsString(); +// +// contractID = convertContractName(contractID); +// +// args.remove("contractID"); +// args.addProperty("contractID", contractID); +// String requestID; +// if (!args.has("requestID")) { +// requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); +// args.addProperty("requestID", requestID); +// } else requestID = args.get("requestID").getAsString(); +// args.remove("action"); +// args.addProperty("action", "executeContractTrustfully"); +// MultiPointContractInfo mInfo = NodeCenterActions.contractID2Members.get(contractID); +// args.addProperty("seq", mInfo.getNextSeq()); +// args.addProperty("needSeq", ContractType.needSeq(mInfo.type)); +// List nodes = mInfo.members; +// logger.debug("tid:" + Thread.currentThread().getId() + " contractID:" + contractID); +// if (nodes == null) { +// rc.onResult( +// "{\"status\":\"Error\",\"result\":\"can't locate contract\",\"action\":\"onExecuteContractTrustfully\"}"); +// } else { +// mInfo.rcf.execute(requestID, rc, JsonUtil.toJson(args)); +// } +// +// // add request cache,only for requestAll type contract +// if (ContractType.needSeq(mInfo.type)) { +// RequestCache reqc = NodeCenterActions.getCache(contractID); +// reqc.put(args.get("seq").getAsString(), args); +// } +// } + + + //only for test ADSP +// public static void testExecuteContract(JsonObject args, final ResultCallback rc) { +// String contractID = args.get("contractID").getAsString(); +// +// contractID = convertContractName(contractID); +// +// args.remove("contractID"); +// args.addProperty("contractID", contractID); +// String requestID; +// if (!args.has("requestID")) { +// requestID = System.currentTimeMillis() + "_" + (int) (Math.random() * 10000); +// args.addProperty("requestID", requestID); +// } else requestID = args.get("requestID").getAsString(); +// args.remove("action"); +// args.addProperty("action", "executeContractTrustfully"); +// MultiPointContractInfo mInfo = NodeCenterActions.contractID2Members.get(contractID); +// System.out.println("mInfo是空吗?" + mInfo == null); +// +// args.addProperty("seq", mInfo.getNextSeq()); +// args.addProperty("needSeq", ContractType.needSeq(mInfo.type)); +// List nodes = mInfo.members; +// logger.debug("tid:" + Thread.currentThread().getId() + " contractID:" + contractID); +// if (nodes == null) { +// rc.onResult( +// "{\"status\":\"Error\",\"result\":\"can't locate contract\",\"action\":\"onExecuteContractTrustfully\"}"); +// } else { +// mInfo.rcf.execute(requestID, rc, JsonUtil.toJson(args)); +// } +// +// //add request cache,only for requestAll type contract +// if(ContractType.needSeq(mInfo.type)){ +// RequestCache reqc = NodeCenterActions.getCache(contractID); +// reqc.put(args.get("seq").getAsString(),args); +// } +// } + + @Action(async = true, userPermission = 1L << 6) + public void listContractProcess(JsonObject args, final ResultCallback rc) { + Map ret = new HashMap<>(); + ret.put("action", "onListContractProcess"); + List info = new ArrayList<>(); + LOGGER.debug( + "[contracts] " + + JsonUtil.toPrettyJson(NodeCenterActions.nodeInfos)); + LOGGER.debug( + "[cid2Nodes]" + + JsonUtil.toPrettyJson(NodeCenterActions.contractID2Members)); + for (String key : NodeCenterActions.contractID2Members.keySet()) { + ContractDesp desp = NodeCenterActions.getContractByID(key); + if (desp != null) info.add(desp); + } + ret.put("data", JsonUtil.toJson(info)); + rc.onResult(JsonUtil.toJson(ret)); + } + + @Action(userPermission = 1 << 6, async = true) + public void listMultiPointContractProcess(JsonObject json, ResultCallback resultCallback) { + Map ret = new HashMap<>(); + ret.put("action", "onListMultiPointContractProcess"); + List info = new ArrayList<>(); + LOGGER.debug( + "[contracts] " + + JsonUtil.toPrettyJson(NodeCenterActions.nodeInfos)); + LOGGER.debug( + "[cid2Nodes]" + + JsonUtil.toPrettyJson(NodeCenterActions.contractID2Members)); + for (String key : NodeCenterActions.contractID2Members.keySet()) { + ContractDesp desp = NodeCenterActions.getContractByID(key); + if (desp != null) info.add(desp); + } + ret.put("data", JsonUtil.toJson(info)); + resultCallback.onResult(JsonUtil.toJson(ret)); + } + + @Action(userPermission = 1 << 6, async = true) + public void stopMultiPointContractProcess(JsonObject json, ResultCallback resultCallback) { + String contractID = json.get("contractID").getAsString(); + MultiPointContractInfo info = NodeCenterActions.contractID2Members.get(contractID); + NodeCenterActions.contractID2Members.remove(contractID); + String req = "{\"action\":\"killContractProcess\",\"contractID\":\"" + contractID + "\"}"; + for (String member : info.members) { + CMNode node; + node = NodeCenterActions.nodeInfos.get(member); + node.connection.controller.sendMsg(req); + } + listMultiPointContractProcess(json, resultCallback); + } + +// @Action(async = true, userPermission = 1L << 6) +// public void startContractMultiPoint(JsonObject args, final ResultCallback rc) { +// Map request = new HashMap(); +// Contract contract = new Contract(); +// if (args.has("type")) { +// contract.setType(ContractType.valueOf(args.get("type").getAsString())); +// } +// +// // this multiPointContractInfo problem +// MultiPointContractInfo multiPointContractInfo = new MultiPointContractInfo(); +// multiPointContractInfo.type = contract.getType(); +// +// logger.debug("startContractMultiPoint:"); +// logger.debug(JsonUtil.toPrettyJson(args)); +// if (args.has("script")) contract.setScript(args.get("script").getAsString()); +// else if (args.has("path")) contract.setScript(args.get("path").getAsString()); +// else if (args.has("projectName")) +// contract.setScript("/" + args.get("projectName").getAsString()); +// // contract.setScript("/" + args.get("projectName").getAsString() + "/manifest.json"); +// +// SM2 sm2 = new SM2(); +// SM2KeyPair sm2Key = sm2.generateKeyPair(); +// contract.setID(sm2Key.getPublicKeyStr().hashCode() + ""); +// contract.setKey(sm2Key.getPrivateKey().toString(16)); +// contract.setPublicKey(sm2Key.getPublicKeyStr()); +// +// contract.setNodeCenterRepoDOI(DOIPMainServer4NC.repoIdentifier); +// // 多点合约部署时,由NC预先注册一个DOI以备使用 +// if (DoConfig.openContractDORegister) { +// try { +// HandleService hs = new HandleService(HandleServiceUtils.hrRegister); +// String tmpDOI = hs.registerContract(DOIPMainServer4NC.repoIdentifier); +// if (tmpDOI == "" || tmpDOI == null) +// contract.setDOI("RegisterFailed"); +// else +// contract.setDOI(tmpDOI); +// } +// catch (Exception e) { +// e.printStackTrace(); +// contract.setDOI("RegisterFailed"); +// } +// } +// +// String[] nodeNames = +// args.get("peersID").getAsString().split(","); // all nodes' peerID in the unit +// Set nodeNameSet = new HashSet<>(); // nodes' peerID +// for (String str : nodeNames) { +// if (str != null && str.length() > 0) nodeNameSet.add(str); +// } +// +// List udpids = new ArrayList<>(); // nodes' udpID in the unit +// +// for (CMNode node : NodeCenterActions.nodeinfos.values()) { +// if (nodeNameSet.contains(node.nodeName)) { +// udpids.add(node.udpID); +// } +// } +// final long curr = System.currentTimeMillis(); +// String requestID = curr + "_" + (int) (Math.random() * 10000); +// NodeCenterActions.ResultCollector collector = +// new NodeCenterActions.ResultCollector( +// requestID, +// new ResultCallback() { +// @Override +// public void onResult(String str) { +// Map ret = new HashMap<>(); +// ret.put("action", "onStartContractTrustfullyResult"); +// ret.put("data", str); +// ret.put("executionTime", (System.currentTimeMillis() - curr) + ""); +// rc.onResult(JsonUtil.toJson(ret)); +// } +// }, +// udpids.size()); +// NodeCenterActions.sync.sleepWithTimeout(requestID, collector, 20); +// +// // ArrayList udpids=gson.fromJson(args.get("members"), +// // new TypeToken>(){}.getType()); +// Map members = new HashMap<>(); // nodes' +// for (int i = 0; i < udpids.size(); i++) { +// members.put( +// udpids.get(i), +// NodeCenterActions.runner.getUDPAddr(Integer.valueOf(udpids.get(i))) +// + ":" +// + (i == 0)); // regard the first node as the master in the unit +// } +// +// List nodes = new ArrayList<>(); // nodes' pubKey +// for (CMNode node : NodeCenterActions.nodeinfos.values()) { +// if (nodeNameSet.contains(node.nodeName)) { +// nodes.add(node.pubKey); +// } +// } +// +// request.put("memberStr", JsonUtil.toJson(members)); +// request.put("isPrivate", args.get("isPrivate").getAsString()); +// request.put("pubKey", managerAction.pubKey); +// request.put("action", "startContractTrustfully"); +// request.put("requestID", requestID); +// multiPointContractInfo.members = nodes; +// +// contract.setNumOfCopies(1); +// switch (multiPointContractInfo.type) { +// case Sole: +// logger.debug("Can't support Solo in multi-point mode"); +// return; +// case RequestOnce: +// multiPointContractInfo.rcf = new RequestOnceExecutor(multiPointContractInfo); +// break; +// case ResponseOnce: +// multiPointContractInfo.rcf = new ResponseOnceExecutor(multiPointContractInfo); +// break; +// case RequestAllResponseFirst: +// multiPointContractInfo.rcf = new RequestAllExecutor(multiPointContractInfo, 1); +// contract.setNumOfCopies(nodes.size()); +// break; +// case RequestAllResponseHalf: +// multiPointContractInfo.rcf = +// new RequestAllExecutor(multiPointContractInfo, nodes.size() / 2 + 1); +// contract.setNumOfCopies(nodes.size()); +// break; +// case RequestAllResponseAll: +// multiPointContractInfo.rcf = +// new RequestAllExecutor(multiPointContractInfo, nodes.size()); +// contract.setNumOfCopies(nodes.size()); +// break; +// } +// NodeCenterActions.contractID2Members.put(contract.getID(), multiPointContractInfo); +// +// request.put("contractStr", JsonUtil.toJson(contract)); +// String startReq = JsonUtil.toJson(request); +// logger.debug("启动合约:" + startReq); +// +// for (String nodeID : nodes) { +// CMNode node; +// node = NodeCenterActions.nodeinfos.get(nodeID); +// node.connection.controller.sendMsg(startReq); +// +// if (!NodeCenterActions.recoverMap.containsKey(nodeID)) { +// NodeCenterActions.recoverMap.put(nodeID, new ConcurrentHashMap<>()); +// } +// ContractRecord record = new ContractRecord(contract.getID()); +// record.members = new HashMap<>(members); +// NodeCenterActions.recoverMap.get(nodeID).put(contract.getID(), record); +// } +// +// rc.onResult( +// "{\"status\":\"Success\",\"result\":\"" +// + contract.getID() +// + "\",\"action\":\"onStartTrustfulContract\"}"); +// } + + private void example() { + for (CMNode node : NodeCenterActions.nodeInfos.values()) { + // node.connection.executeContractTrustfully(args, rc); + } + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/searchresult/ContractMeta.java b/src/main/java/org/bdware/server/nodecenter/searchresult/ContractMeta.java new file mode 100644 index 0000000..ecab9b9 --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/searchresult/ContractMeta.java @@ -0,0 +1,104 @@ +package org.bdware.server.nodecenter.searchresult; + +public class ContractMeta { + private String contractID; + private String name; + private String status; + private String pubkey; + private String owner; + private String readmeStr; + private String nodeAddr; + private String pngUrl; + private String doi; + private long buildTime; + private String doip; + + + public String getContractID() { + return contractID; + } + + public void setContractID(String contractID) { + this.contractID = contractID; + } + + public String getPubkey() { + return pubkey; + } + + public void setPubkey(String pubkey) { + this.pubkey = pubkey; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getReadmeStr() { + return readmeStr; + } + + public void setReadmeStr(String readmeStr) { + this.readmeStr = readmeStr; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getNodeAddr() { + return nodeAddr; + } + + public void setNodeAddr(String nodeAddr) { + this.nodeAddr = nodeAddr; + } + + public String getPngUrl() { + return pngUrl; + } + + public void setPngUrl(String pngUrl) { + this.pngUrl = pngUrl; + } + + public String getDoi() { + return doi; + } + + public void setDoi(String doi) { + this.doi = doi; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getBuildTime() { + return buildTime; + } + + public void setBuildTime(long buildTime) { + this.buildTime = buildTime; + } + + public String getDoip() { + return doip; + } + + public void setDoip(String doip) { + this.doip = doip; + } +} diff --git a/src/main/java/org/bdware/server/nodecenter/searchresult/ResultModel.java b/src/main/java/org/bdware/server/nodecenter/searchresult/ResultModel.java new file mode 100644 index 0000000..b878f8b --- /dev/null +++ b/src/main/java/org/bdware/server/nodecenter/searchresult/ResultModel.java @@ -0,0 +1,42 @@ +package org.bdware.server.nodecenter.searchresult; + +import java.util.List; + +public class ResultModel { + private List contractMetaList; + private Long contractCount; + private Long pageCount; + private long curPage; + + public List getContractMetaList() { + return contractMetaList; + } + + public void setContractMetaList(List contractMetaList) { + this.contractMetaList = contractMetaList; + } + + public Long getContractCount() { + return contractCount; + } + + public void setContractCount(Long contractCount) { + this.contractCount = contractCount; + } + + public Long getPageCount() { + return pageCount; + } + + public void setPageCount(Long pageCount) { + this.pageCount = pageCount; + } + + public long getCurPage() { + return curPage; + } + + public void setCurPage(long curPage) { + this.curPage = curPage; + } +} diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties new file mode 100644 index 0000000..acc1cfb --- /dev/null +++ b/src/main/resources/log4j.properties @@ -0,0 +1,8 @@ +### 设置### +log4j.rootLogger = info,debug,stdout + +### 输出信息到控制抬 ### +log4j.appender.stdout = org.apache.log4j.ConsoleAppender +log4j.appender.stdout.Target = System.out +log4j.appender.stdout.layout = org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{HH:mm:ss.SSS} %m (%F:%L)[%M]%n \ No newline at end of file diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties new file mode 100644 index 0000000..522e9f4 --- /dev/null +++ b/src/main/resources/log4j2.properties @@ -0,0 +1,15 @@ +filter.threshold.type=ThresholdFilter +filter.threshold.level=debug +appender.console.type=Console +appender.console.name=STDOUT +appender.console.layout.type=PatternLayout +appender.console.layout.pattern=%highlight{[%-5p] %d{HH:mm:ss.SSS} %m (%F:%L)[%M]%n}{FATAL=Bright Red,ERROR=Red,WARN=Yellow,INFO=Green,DEBUG=Blue,TRACE=White} +appender.rolling.type=File +appender.rolling.name=log +appender.rolling.append=true +appender.rolling.fileName=./log/nc.log +appender.rolling.layout.type=PatternLayout +appender.rolling.layout.pattern=%d-%m%n +rootLogger.level=info +rootLogger.appenderRef.stdout.ref=STDOUT +rootLogger.appenderRef.log.ref=log \ No newline at end of file diff --git a/src/main/resources/org/bdware/server/stopwords.txt b/src/main/resources/org/bdware/server/stopwords.txt new file mode 100644 index 0000000..2b99616 --- /dev/null +++ b/src/main/resources/org/bdware/server/stopwords.txt @@ -0,0 +1,63 @@ +////////// Punctuation tokens to remove //////////////// +, +. +` +- +_ += +? +' +| +" +( +) +{ +} +[ +] +< +> +* +# +& +^ +$ +@ +! +~ +: +; ++ +/ +\ +《 +》 +— +- +, +。 +、 +: +; +! +· +? +“ +” +) +( +【 +】 +[ +] +● +// the line below contains an IDEOGRAPHIC SPACE character (Used as a space in Chinese) +  + +//////////////// English Stop Words //////////////// + +//////////////// Chinese Stop Words //////////////// +的 +我 +是 +想 diff --git a/src/test/java/org/bdware/bdserver/IRPTest.java b/src/test/java/org/bdware/bdserver/IRPTest.java new file mode 100644 index 0000000..d479400 --- /dev/null +++ b/src/test/java/org/bdware/bdserver/IRPTest.java @@ -0,0 +1,127 @@ +package org.bdware.bdserver; + +import com.google.gson.Gson; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.bdware.doip.core.exception.IrpClientException; +import org.bdware.doip.core.model.handleRecord.DoHandleRecord; +import org.bdware.doip.core.model.handleRecord.DoipServiceHandleRecord; +import org.bdware.doip.core.model.handleRecord.HandleRecordBase; +import org.bdware.doip.core.utils.GlobalConfigurations; +import org.bdware.doip.endpoint.irpClient.InternalIrpClient; +import org.bdware.doip.endpoint.irpClient.IrpClient; +import org.junit.Before; +import org.junit.Test; + + +public class IRPTest { + private static final Logger LOGGER = LogManager.getLogger(IRPTest.class); + IrpClient client; + + @Before + public void createTestClient() { + GlobalConfigurations.LHS_Address = "http://127.0.0.1:18007/"; + client = new InternalIrpClient(null, "86.5000.470/dou.SUPER", GlobalConfigurations.LHS_Address); + } + + @Test + public void testReslove() { + HandleRecordBase handleRecordBase = null; + try { + handleRecordBase = client.resolve("86.5000.470/doip.iZWUqzwFtY_bdw"); + } catch (IrpClientException e) { + e.printStackTrace(); + } + LOGGER.info(new Gson().toJson(handleRecordBase)); + } + + @Test + public void testRegister() { + DoHandleRecord doHr = new DoHandleRecord(GlobalConfigurations.User_Handle, "null"); + DoipServiceHandleRecord doipServiceHandleRecord = new DoipServiceHandleRecord("1", "2", "3", "4", "5"); + String handle = "86.5000.470/do.yWkNN8otdv_bdw"; +// doHr.handle = handle; + try { + handle = client.register(doipServiceHandleRecord); + LOGGER.debug(handle); + //test resolve + HandleRecordBase handleRecordBase = client.resolve(handle); + LOGGER.info(new Gson().toJson(handleRecordBase)); + } catch (IrpClientException e) { + e.printStackTrace(); + } + } + + @Test + public void testUnregister() { + String handle = "86.5000.470/do.A0gOqH2hup_bdw"; + String resp = client.unRegister(handle); + LOGGER.info(resp); + } + + @Test + public void testReRegister() { + DoHandleRecord doHr = new DoHandleRecord(GlobalConfigurations.User_Handle, "12345"); + String handle = "86.5000.470/do.Y1LnYqyH5F_bdw"; + doHr.handle = handle; + try { + handle = client.reRegister(doHr); + LOGGER.debug(handle); + //test resolve + HandleRecordBase handleRecordBase = client.resolve(handle); + LOGGER.debug(new Gson().toJson(handleRecordBase)); + } catch (IrpClientException e) { + e.printStackTrace(); + } + } + + @Test + public void pumpDataIntoLhs() { + DoHandleRecord doHr = new DoHandleRecord(GlobalConfigurations.User_Handle, "null"); + DoipServiceHandleRecord doipServiceHandleRecord = new DoipServiceHandleRecord("1", "2", "3", "4", "5"); + String handle; +// doHr.handle = handle; + try { + for (int i = 0; i < 20; i++) { + handle = client.register(doipServiceHandleRecord); + LOGGER.info(handle); + handle = client.register(doHr); + LOGGER.info(handle); + } +// handle = client.register(doipServiceHandleRecord); +// logger.debug(handle); +// //test resolve +// HandleRecordBase handleRecordBase = client.resolve(handle); +// logger.info(new Gson().toJson(handleRecordBase)); + } catch (IrpClientException e) { + e.printStackTrace(); + } + } + +// @Test +// public void updateUserHandleRecord(){ +// if (Security.getProvider("BC") == null) { +// Security.addProvider(new BouncyCastleProvider()); +// } +// +// try { +// GlobalCertifications.loadKeysFromJKS("keys/dou.SUPER.keystore", GlobalConfigurations.certPassword); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// +// GlobalIrpClient.useGeneralIrpClient(GlobalCertifications.localKeypair +// , GlobalConfigurations.User_Handle +// , GlobalConfigurations.LHS_Address); +// +// UserHandleRecord usr = new UserHandleRecord(JWK.getJWKFormatPK(GlobalCertifications.getGlobalKeypair().getPublic()),"SUPER USER"); +// usr.handle = "86.5000.470/dou.SUPER"; +// try { +// String out = client.reRegister(usr); +// logger.debug(out); +// logger.debug("Resolution: " + client.resolve(usr.handle)); +// } catch (IrpClientException e) { +// e.printStackTrace(); +// } +// } +} diff --git a/src/test/java/org/bdware/bdserver/PermissionHelper.java b/src/test/java/org/bdware/bdserver/PermissionHelper.java new file mode 100644 index 0000000..1ccab1f --- /dev/null +++ b/src/test/java/org/bdware/bdserver/PermissionHelper.java @@ -0,0 +1,82 @@ +package org.bdware.bdserver; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import org.bdware.sc.conn.ResultCallback; +import org.bdware.server.action.Action; +import org.bdware.server.action.ActionExecutor; +import org.bdware.server.nodecenter.NCHttpHandler; +import org.bdware.server.nodecenter.NodeCenterWSFrameHandler; +import org.bdware.server.permission.Role; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.util.*; + +public class PermissionHelper { + static class Line { + String action; + transient long permission; + String roles; + } + + @Test + public void listClusterHttpAction() { + ActionExecutor ae = NCHttpHandler.getActionExecutor(); + Map> handlers = ae.getHandlers(); + EnumSet set = EnumSet.allOf(Role.class); + set.remove(Role.ContractInstanceManager); + set.remove(Role.ContractProvider); + set.remove(Role.ContractUser); + printActions(handlers, set); + } + + @Test + public void listClusterWSAction() { + NodeCenterWSFrameHandler handler = new NodeCenterWSFrameHandler(); + Map> handlers = handler.getAE().getHandlers(); + EnumSet set = EnumSet.allOf(Role.class); + set.remove(Role.ContractInstanceManager); + set.remove(Role.ContractProvider); + set.remove(Role.ContractUser); + + printActions(handlers, set); + } + + private void printActions( + Map> handlers, EnumSet set) { + List lines = new ArrayList<>(); + for (String str : handlers.keySet()) { + Method m = handlers.get(str).first(); + Action a = m.getAnnotation(Action.class); + Line l = new Line(); + lines.add(l); + l.action = str; + l.permission = a.userPermission(); + l.roles = ""; + for (Role r : set) + if ((r.getValue() & l.permission) == l.permission) l.roles += r.name() + ";"; + if (l.roles.equals("CenterManager;NodeManager;Node;Anonymous;")) l.roles = "任意角色"; + } + lines.sort( + new Comparator() { + @Override + public int compare(Line o1, Line o2) { + return o1.action.compareTo(o2.action); + } + }); + lines.sort( + new Comparator() { + @Override + public int compare(Line o1, Line o2) { + return o1.roles.compareTo(o2.roles); + } + }); + + Gson g = new GsonBuilder().setPrettyPrinting().create(); + for (Line l : lines) { + System.out.println("| " + l.action + " | | " + l.roles + " |"); + } + } +} diff --git a/src/test/java/org/bdware/bdserver/TestServer.java b/src/test/java/org/bdware/bdserver/TestServer.java new file mode 100644 index 0000000..8f34361 --- /dev/null +++ b/src/test/java/org/bdware/bdserver/TestServer.java @@ -0,0 +1,31 @@ +package org.bdware.bdserver; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class TestServer { + private static final Logger LOGGER = LogManager.getLogger(TestServer.class); + + static class TestHandler extends SimpleChannelInboundHandler { + TestHandler() { + LOGGER.info("new instance"); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) { + LOGGER.info("active"); + System.out.println("======Active======="); + ctx.channel() + .writeAndFlush("ACTIVE from Server") + .addListener(future -> LOGGER.info("Active Send Done!!")); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { + LOGGER.info("receive"); + } + } +} diff --git a/src/test/java/org/bdware/bdserver/test/TestADSP.java b/src/test/java/org/bdware/bdserver/test/TestADSP.java new file mode 100644 index 0000000..583fa62 --- /dev/null +++ b/src/test/java/org/bdware/bdserver/test/TestADSP.java @@ -0,0 +1,192 @@ +package org.bdware.bdserver.test; + +public class TestADSP { + // public static Map nodes = new HashMap(); +// +// private static volatile Boolean flag = false; +// private static int totalRequest = 10; +// private static int nodeCount = 5; +// +// public static String resultPath = "./testADSP/" + "result.txt"; +// public static String contractID = "counter"; +// public static String action = "executeContract"; +// public static String pubkey = "0480204f4ef341359a5f64fcb11baf9ca2e6706ac20cba36ca83066870cf2c1d5de6df67e24e68dde7934af9b31d94a6084281db3d32d5ce42ab8f75bf799aca05"; +// +// public static String operation = "count"; +// public static String arg = "1"; +// +// public static String priKey = "63464fa9587bbf2a022b3d655f4ed49c7d9a4249de1079d52bd5a1fec3308719"; +// public static String signature = "dfd07c00c5c89fb0f901bed132db4342f49f4560f34c15878636623d2a8716a2c93ad32eeb9ee9c4f1d905df292cb4a1a27aa0171f2856848ce07cfc8022e809"; +// +// public static List crashSeries = new ArrayList(); +// public static AtomicInteger crashIndex = new AtomicInteger(); +// +// public static ConcurrentSet crashSet = new ConcurrentSet(); +// public static ConcurrentSet recover = new ConcurrentSet(); +// +// public static TestTask tt; +// public static int period = 3000; +// +// static{ +// getSig(); +// +// nodes.put("zyx's book","0440023d13facddcefbd87f2bb622b1b2b6ca43eb84c0579b82450814233ce557bd7ad7852cd47f1d6df867c5413ecf3dd1954cdbf5a6da683e3a89095f091d83d"); +// nodes.put("localNode1","0480f783cf63294224afbd19e175e5c7ea0c5c60ee115ed9e114fe2691eb28cc1680e5fec532d64c80d2f6b737e8b43b94d5a9c73206ac6235c50ff992133e4d38"); +// nodes.put("localNode2","044e0c127e24407ee8a8abd4fe507b32b340ce9775317b8d7b5bb8e182745d6a45c57aaf867d80a5f816a7561564f9294c6aee5916f95e93c0011e16c28b9e849a"); +// nodes.put("localNode3","0485040cfd94bec672bb8ba184856188963ee4ad339d247a76e2d9819f20e61bfad24ec763b1371998583f9dc0cf55c4d53cb1a2ec84c67aed1aa5203cc84fc78f"); +// nodes.put("localNode4",""); +// nodes.put("localNode5",""); +// +// +// //生成崩溃序列 +// crashSeries.clear(); +// for(int i = 1;i <= totalRequest;i++){ +// int temp = i % nodeCount; +// if(temp == 0) +// temp = nodeCount; +// crashSeries.add("crash localNode" + temp); +// crashSeries.add("recover localNode" + temp); +// } +// +// System.out.println("崩溃序列" + crashSeries.size()); +// +// Timer timer = new Timer(); +// tt = new TestTask(); +// timer.schedule(tt, new Date(), period); +// } +// +// static class TestTask extends TimerTask { +// @Override +// public void run() { +// if(TestADSP.getFlag()){ +// int index = TestADSP.crashIndex.getAndIncrement(); +// System.out.println("[TestADSP] index=" + TestADSP.crashIndex.get()); +// +// String[] crash = TestADSP.crashSeries.get(index).split(" "); +// String pubKey = convertNameToPubkey(crash[1]); +// switch (crash[0]){ +// case "crash": +// if(pubKey != null){ +// System.out.println("[TestADSP] 令节点 " + crash[1] + "崩溃"); +// System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()))); +// crashSet.add(pubKey); +// } +// break; +// case "recover": +// if(pubKey != null){ +// if(crashSet.contains(pubKey)){ +// crashSet.remove(pubKey); +// } +// recover.add(pubKey); +// System.out.println("[TestADSP] 令节点 " + crash[1] + "上线"); +// System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()))); +// } +// break; +// default: +// break; +// } +// +// +// try { +// Thread.sleep(period / 2); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// +// +// +// System.out.println("[TestADSP]发起请求 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis()))); +// JsonObject jo = new JsonObject(); +// jo.addProperty("action", TestADSP.action); +// jo.addProperty("contractID", TestADSP.contractID); +// jo.addProperty("operation", TestADSP.operation); +// jo.addProperty("arg", TestADSP.arg); +// jo.addProperty("pubkey",TestADSP.pubkey); +// jo.addProperty("signature",TestADSP.signature); +// UnitActions.testExecuteContract(jo,null); +// +// if(index == (TestADSP.crashSeries.size() - 1)){ +// System.out.println("测试停止!"); +// TestADSP.setFlag(false); +// //TestADSP.check(); +// } +// }//if +// } +// } +// +// public static boolean getFlag(){ +// boolean temp; +// synchronized (flag){ +// temp = flag; +// } +// return temp; +// } +// +// public static void setFlag(boolean temp){ +// synchronized (flag){ +// flag = temp; +// System.out.println("[TestADSP]设置flag为" + flag); +// } +// } +// +// public static void getSig(){ +// String temp = contractID + "|" + operation + "|" + arg + "|" + pubkey; +// SM2 sm2 = new SM2(); +// signature = sm2.sign(temp.getBytes(),priKey).toString(); +// } +// +// //查看结果输出文件 +// public static void check() { +// tt.cancel(); +// +// int correct = 0; +// int incorrect = 0; +// int unavailable = 0; +// int fileTotal = 0; +// +// File file = new File("./front-cluster/testADSP/result.txt"); +// System.out.println(file.getAbsolutePath()); +// StringBuilder result = new StringBuilder(); +// try{ +// BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));//构造一个BufferedReader类来读取文件 +// +// String s = null; +// while((s = br.readLine())!=null){//使用readLine方法,一次读一行 +// if(!s.equals("")){ +// fileTotal++; +// String str[] = s.split(" "); +// if(str[1].equals((Integer.parseInt(str[0]) + 1) + ".0")){ +// correct++; +// }else{ +// incorrect++; +// } +// } +// } +// br.close(); +// +// }catch(Exception e){ +// e.printStackTrace(); +// } +// +// unavailable = totalRequest * 2 - fileTotal; +// +// System.out.println("共有" + totalRequest * 2 + "次请求,其中" + correct + "正确," + incorrect + "不正确," + unavailable + "不可用."); +// } +// +// public static String convertNameToPubkey(String nodeName){ +// return nodes.get(nodeName); +// } +// +// public static String convertPubKeyToName(String pubkey){ +// for(String key : nodes.keySet()){ +// if(nodes.get(key).equals(pubkey)){ +// return key; +// } +// } +// return null; +// } +// +// public static void main(String[] args){ +// check(); +// } +} diff --git a/src/test/java/org/bdware/bdserver/test/TestSearch.java b/src/test/java/org/bdware/bdserver/test/TestSearch.java new file mode 100644 index 0000000..08f7bee --- /dev/null +++ b/src/test/java/org/bdware/bdserver/test/TestSearch.java @@ -0,0 +1,84 @@ +package org.bdware.bdserver.test; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.queryparser.classic.QueryParser; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.bdware.sc.http.HttpUtil; +import org.bdware.sc.util.JsonUtil; +import org.junit.Test; + +import java.nio.file.Paths; + +public class TestSearch { + private static final Logger LOGGER = LogManager.getLogger(TestSearch.class); + + @Test + public void test() { + String url = "http://127.0.0.1:18005/doip?pubkey=04&owner=0&contractID=0&readmeStr=0"; + LOGGER.info(JsonUtil.toJson(HttpUtil.httpGet(url))); + } + + @Test + public void testIndexSearch() throws Exception { + + //1. 创建分词器(对搜索的关键词进行分词使用) + //注意: 分词器要和创建索引的时候使用的分词器一模一样 + Analyzer analyzer = new StandardAnalyzer(); + + //2. 创建查询对象, + //第一个参数: 默认查询域, 如果查询的关键字中带搜索的域名, 则从指定域中查询, 如果不带域名则从, 默认搜索域中查询 + //第二个参数: 使用的分词器 + QueryParser queryParser = new QueryParser("name", analyzer); + + //3. 设置搜索关键词 + //华 OR 为 手 机 + Query query = queryParser.parse("华为手机"); + + //4. 创建Directory目录对象, 指定索引库的位置 + Directory dir = FSDirectory.open(Paths.get("E:\\dir")); + //5. 创建输入流对象 + IndexReader indexReader = DirectoryReader.open(dir); + //6. 创建搜索对象 + IndexSearcher indexSearcher = new IndexSearcher(indexReader); + //7. 搜索, 并返回结果 + //第二个参数: 是返回多少条数据用于展示, 分页使用 + TopDocs topDocs = indexSearcher.search(query, 10); + + //获取查询到的结果集的总数, 打印 + System.out.println("=======count=======" + topDocs.totalHits); + + //8. 获取结果集 + ScoreDoc[] scoreDocs = topDocs.scoreDocs; + + //9. 遍历结果集 + if (scoreDocs != null) { + for (ScoreDoc scoreDoc : scoreDocs) { + //获取查询到的文档唯一标识, 文档id, 这个id是lucene在创建文档的时候自动分配的 + int docID = scoreDoc.doc; + //通过文档id, 读取文档 + Document doc = indexSearcher.doc(docID); + System.out.println("=================================================="); + //通过域名, 从文档中获取域值 + System.out.println("===id==" + doc.get("id")); + System.out.println("===name==" + doc.get("name")); + System.out.println("===price==" + doc.get("price")); + System.out.println("===image==" + doc.get("image")); + System.out.println("===brandName==" + doc.get("brandName")); + System.out.println("===categoryName==" + doc.get("categoryName")); + + } + } + //10. 关闭流 + } +} diff --git a/src/test/java/org/bdware/bdserver/testLucene/TestIndexManager.txt b/src/test/java/org/bdware/bdserver/testLucene/TestIndexManager.txt new file mode 100644 index 0000000..63453e6 --- /dev/null +++ b/src/test/java/org/bdware/bdserver/testLucene/TestIndexManager.txt @@ -0,0 +1,246 @@ +package org.bdware.bdserver.testLucene; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.apache.lucene.document.*; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.Term; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.junit.Test; + +import javax.json.JsonObject; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +public class TestManager { + + /** + * 创建索引库 + */ + @Test + public void createIndexTest() throws Exception { + //1. 采集数据 + SkuDao skuDao = new SkuDaoImpl(); + List skuList = skuDao.querySkuList(); + + //文档集合 + List docList = new ArrayList<>(); + + for (Sku sku : skuList) { + //2. 创建文档对象 + Document document = new Document(); + + //创建域对象并且放入文档对象中 + /** + * 是否分词: 否, 因为主键分词后无意义 + * 是否索引: 是, 如果根据id主键查询, 就必须索引 + * 是否存储: 是, 因为主键id比较特殊, 可以确定唯一的一条数据, 在业务上一般有重要所用, 所以存储 + * 存储后, 才可以获取到id具体的内容 + */ + document.add(new StringField("id", sku.getId(), Field.Store.YES)); + + /** + * 是否分词: 是, 因为名称字段需要查询, 并且分词后有意义所以需要分词 + * 是否索引: 是, 因为需要根据名称字段查询 + * 是否存储: 是, 因为页面需要展示商品名称, 所以需要存储 + */ + document.add(new TextField("name", sku.getName(), Field.Store.YES)); + + /** + * 是否分词: 是(因为lucene底层算法规定, 如果根据价格范围查询, 必须分词) + * 是否索引: 是, 需要根据价格进行范围查询, 所以必须索引 + * 是否存储: 是, 因为页面需要展示价格 + */ + document.add(new IntPoint("price", sku.getPrice())); + document.add(new StoredField("price", sku.getPrice())); + + /** + * 是否分词: 否, 因为不查询, 所以不索引, 因为不索引所以不分词 + * 是否索引: 否, 因为不需要根据图片地址路径查询 + * 是否存储: 是, 因为页面需要展示商品图片 + */ + document.add(new StoredField("image", sku.getImage())); + + /** + * 是否分词: 否, 因为分类是专有名词, 是一个整体, 所以不分词 + * 是否索引: 是, 因为需要根据分类查询 + * 是否存储: 是, 因为页面需要展示分类 + */ + document.add(new StringField("categoryName", sku.getCategoryName(), Field.Store.YES)); + + /** + * 是否分词: 否, 因为品牌是专有名词, 是一个整体, 所以不分词 + * 是否索引: 是, 因为需要根据品牌进行查询 + * 是否存储: 是, 因为页面需要展示品牌 + */ + document.add(new StringField("brandName", sku.getBrandName(), Field.Store.YES)); + + //将文档对象放入到文档集合中 + docList.add(document); + } + //3. 创建分词器, StandardAnalyzer标准分词器, 对英文分词效果好, 对中文是单字分词, 也就是一个字就认为是一个词. + Analyzer analyzer = new IKAnalyzer(); + //4. 创建Directory目录对象, 目录对象表示索引库的位置 + Directory dir = FSDirectory.open(Paths.get("E:\\dir")); + //5. 创建IndexWriterConfig对象, 这个对象中指定切分词使用的分词器 + IndexWriterConfig config = new IndexWriterConfig(analyzer); + //6. 创建IndexWriter输出流对象, 指定输出的位置和使用的config初始化对象 + IndexWriter indexWriter = new IndexWriter(dir, config); + //7. 写入文档到索引库 + for (Document doc : docList) { + indexWriter.addDocument(doc); + } + //8. 释放资源 + indexWriter.close(); + } + + /** + * 索引库修改操作 + * @throws Exception + */ + @Test + public void updateIndexTest() throws Exception { + //需要变更成的内容 + Document document = new Document(); + + document.add(new StringField("id", "100000003145", Field.Store.YES)); + document.add(new TextField("name", "xxxx", Field.Store.YES)); + document.add(new IntPoint("price", 123)); + document.add(new StoredField("price", 123)); + document.add(new StoredField("image", "xxxx.jpg")); + document.add(new StringField("categoryName", "手机", Field.Store.YES)); + document.add(new StringField("brandName", "华为", Field.Store.YES)); + + + //3. 创建分词器, StandardAnalyzer标准分词器, 对英文分词效果好, 对中文是单字分词, 也就是一个字就认为是一个词. + Analyzer analyzer = new StandardAnalyzer(); + //4. 创建Directory目录对象, 目录对象表示索引库的位置 + Directory dir = FSDirectory.open(Paths.get("E:\\dir")); + //5. 创建IndexWriterConfig对象, 这个对象中指定切分词使用的分词器 + IndexWriterConfig config = new IndexWriterConfig(analyzer); + //6. 创建IndexWriter输出流对象, 指定输出的位置和使用的config初始化对象 + IndexWriter indexWriter = new IndexWriter(dir, config); + + + //修改, 第一个参数: 修改条件, 第二个参数: 修改成的内容 + indexWriter.updateDocument(new Term("id", "100000003145"), document); + + + //8. 释放资源 + indexWriter.close(); + } + + /** + * 测试根据条件删除 + * @throws Exception + */ + @Test + public void deleteIndexTest() throws Exception { + //3. 创建分词器, StandardAnalyzer标准分词器, 对英文分词效果好, 对中文是单字分词, 也就是一个字就认为是一个词. + Analyzer analyzer = new StandardAnalyzer(); + //4. 创建Directory目录对象, 目录对象表示索引库的位置 + Directory dir = FSDirectory.open(Paths.get("E:\\dir")); + //5. 创建IndexWriterConfig对象, 这个对象中指定切分词使用的分词器 + IndexWriterConfig config = new IndexWriterConfig(analyzer); + //6. 创建IndexWriter输出流对象, 指定输出的位置和使用的config初始化对象 + IndexWriter indexWriter = new IndexWriter(dir, config); + + + //测试根据条件删除 + //indexWriter.deleteDocuments(new Term("id", "100000003145")); + + //测试删除所有内容 + indexWriter.deleteAll(); + + //8. 释放资源 + indexWriter.close(); + } + + + /** + * 测试创建索引速度优化 + * @throws Exception + */ + @Test + public void createIndexTest2() throws Exception { + //1. 采集数据 + SkuDao skuDao = new SkuDaoImpl(); + List skuList = skuDao.querySkuList(); + + //文档集合 + List docList = new ArrayList<>(); + + for (Sku sku : skuList) { + //2. 创建文档对象 + Document document = new Document(); + document.add(new StringField("id", sku.getId(), Field.Store.YES)); + document.add(new TextField("name", sku.getName(), Field.Store.YES)); + document.add(new IntPoint("price", sku.getPrice())); + document.add(new StoredField("price", sku.getPrice())); + document.add(new StoredField("image", sku.getImage())); + document.add(new StringField("categoryName", sku.getCategoryName(), Field.Store.YES)); + document.add(new StringField("brandName", sku.getBrandName(), Field.Store.YES)); + + //将文档对象放入到文档集合中 + docList.add(document); + } + + long start = System.currentTimeMillis(); + + //3. 创建分词器, StandardAnalyzer标准分词器, 对英文分词效果好, 对中文是单字分词, 也就是一个字就认为是一个词. + Analyzer analyzer = new StandardAnalyzer(); + //4. 创建Directory目录对象, 目录对象表示索引库的位置 + Directory dir = FSDirectory.open(Paths.get("E:\\dir")); + //5. 创建IndexWriterConfig对象, 这个对象中指定切分词使用的分词器 + /** + * 没有优化 小100万条数据, 创建索引需要7725ms + * + */ + IndexWriterConfig config = new IndexWriterConfig(analyzer); + //设置在内存中多少个文档向磁盘中批量写入一次数据 + //如果设置的数字过大, 会过多消耗内存, 但是会提升写入磁盘的速度 + //config.setMaxBufferedDocs(500000); + //6. 创建IndexWriter输出流对象, 指定输出的位置和使用的config初始化对象 + IndexWriter indexWriter = new IndexWriter(dir, config); + //设置多少给文档合并成一个段文件,数值越大索引速度越快, 搜索速度越慢; 值越小索引速度越慢, 搜索速度越快 + //indexWriter.forceMerge(1000000); + //7. 写入文档到索引库 + for (Document doc : docList) { + indexWriter.addDocument(doc); + } + //8. 释放资源 + indexWriter.close(); + long end = System.currentTimeMillis(); + System.out.println("=====消耗的时间为:==========" + (end - start) + "ms"); + } + + private String getRmAsString(JsonObject arg) { + try { + ContractMeta meta = + CMActions.manager.statusRecorder.getContractMeta( + arg.get("contractID").getAsString()); + String path = arg.get("assets/README.md").getAsString(); + ZipFile zipFile = new ZipFile(new File(meta.contract.getScriptStr())); + ZipEntry zipEntry = zipFile.getEntry(path); + InputStream sc = loadAsInputStream(path); + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + byte[] buff = new byte[1024]; + for (int k = 0; (k = sc.read(buff)) > 0; ) { + bo.write(buff, 0, k); + } + return new String(bo.toByteArray()); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/test/java/org/bdware/bdserver/testLucene/WordSegmentationTest.java b/src/test/java/org/bdware/bdserver/testLucene/WordSegmentationTest.java new file mode 100644 index 0000000..5bd2485 --- /dev/null +++ b/src/test/java/org/bdware/bdserver/testLucene/WordSegmentationTest.java @@ -0,0 +1,12 @@ +package org.bdware.bdserver.testLucene; + +import org.bdware.server.nodecenter.MetaIndexAction; +import org.junit.Test; + +public class WordSegmentationTest { + @Test + public void go(){ + MetaIndexAction i = new MetaIndexAction(); + i.segmentWord(null,null); + } +} diff --git a/temp/CoinUseShading_2021-07-05-22_37_45_Auto.ypk_NC b/temp/CoinUseShading_2021-07-05-22_37_45_Auto.ypk_NC new file mode 100644 index 0000000..4c92904 Binary files /dev/null and b/temp/CoinUseShading_2021-07-05-22_37_45_Auto.ypk_NC differ diff --git a/temp/CoinUseShading_2021-07-05-23_29_21_Auto.ypk_NC b/temp/CoinUseShading_2021-07-05-23_29_21_Auto.ypk_NC new file mode 100644 index 0000000..f7ce5ea Binary files /dev/null and b/temp/CoinUseShading_2021-07-05-23_29_21_Auto.ypk_NC differ diff --git a/temp/Counter_2020-12-04-14:39:45_Auto.ypk_NC b/temp/Counter_2020-12-04-14:39:45_Auto.ypk_NC new file mode 100644 index 0000000..b7451cf Binary files /dev/null and b/temp/Counter_2020-12-04-14:39:45_Auto.ypk_NC differ diff --git a/temp/Hello_2021-06-05-21:30:43_Auto.ypk_NC b/temp/Hello_2021-06-05-21:30:43_Auto.ypk_NC new file mode 100644 index 0000000..1b93ce0 Binary files /dev/null and b/temp/Hello_2021-06-05-21:30:43_Auto.ypk_NC differ diff --git a/temp/Script_749432603.ypk_NC b/temp/Script_749432603.ypk_NC new file mode 100644 index 0000000..d616f73 Binary files /dev/null and b/temp/Script_749432603.ypk_NC differ diff --git a/temp/TEMP_2021-06-07-16_28_26_Auto.ypk_NC b/temp/TEMP_2021-06-07-16_28_26_Auto.ypk_NC new file mode 100644 index 0000000..36d4a08 Binary files /dev/null and b/temp/TEMP_2021-06-07-16_28_26_Auto.ypk_NC differ diff --git a/temp/stateFiles/1593916180187_NC b/temp/stateFiles/1593916180187_NC new file mode 100644 index 0000000..ef89462 Binary files /dev/null and b/temp/stateFiles/1593916180187_NC differ diff --git a/temp/stateFiles/1593916214495_NC b/temp/stateFiles/1593916214495_NC new file mode 100644 index 0000000..0740ce7 Binary files /dev/null and b/temp/stateFiles/1593916214495_NC differ diff --git a/temp/stateFiles/1593924239336_NC b/temp/stateFiles/1593924239336_NC new file mode 100644 index 0000000..783c45a Binary files /dev/null and b/temp/stateFiles/1593924239336_NC differ diff --git a/temp/stateFiles/1593924239381_NC b/temp/stateFiles/1593924239381_NC new file mode 100644 index 0000000..04f2096 Binary files /dev/null and b/temp/stateFiles/1593924239381_NC differ diff --git a/temp/stateFiles/1593924239413_NC b/temp/stateFiles/1593924239413_NC new file mode 100644 index 0000000..9c37fd7 Binary files /dev/null and b/temp/stateFiles/1593924239413_NC differ diff --git a/temp/stateFiles/1593925036315_NC b/temp/stateFiles/1593925036315_NC new file mode 100644 index 0000000..bce0818 Binary files /dev/null and b/temp/stateFiles/1593925036315_NC differ diff --git a/temp/stateFiles/1593925036321_NC b/temp/stateFiles/1593925036321_NC new file mode 100644 index 0000000..bce0818 Binary files /dev/null and b/temp/stateFiles/1593925036321_NC differ diff --git a/temp/stateFiles/1593925036323_NC b/temp/stateFiles/1593925036323_NC new file mode 100644 index 0000000..bce0818 Binary files /dev/null and b/temp/stateFiles/1593925036323_NC differ diff --git a/temp/stateFiles/1593941646859_NC b/temp/stateFiles/1593941646859_NC new file mode 100644 index 0000000..7b8620c Binary files /dev/null and b/temp/stateFiles/1593941646859_NC differ diff --git a/temp/stateFiles/1593942529107_NC b/temp/stateFiles/1593942529107_NC new file mode 100644 index 0000000..e162090 Binary files /dev/null and b/temp/stateFiles/1593942529107_NC differ diff --git a/temp/stateFiles/1593947978273_NC b/temp/stateFiles/1593947978273_NC new file mode 100644 index 0000000..68c6a36 Binary files /dev/null and b/temp/stateFiles/1593947978273_NC differ diff --git a/temp/stateFiles/1593948182712_NC b/temp/stateFiles/1593948182712_NC new file mode 100644 index 0000000..7a7c4b0 Binary files /dev/null and b/temp/stateFiles/1593948182712_NC differ diff --git a/temp/stateFiles/1593948382831_NC b/temp/stateFiles/1593948382831_NC new file mode 100644 index 0000000..772b836 Binary files /dev/null and b/temp/stateFiles/1593948382831_NC differ diff --git a/temp/stateFiles/1593948401893_NC b/temp/stateFiles/1593948401893_NC new file mode 100644 index 0000000..8998b60 Binary files /dev/null and b/temp/stateFiles/1593948401893_NC differ diff --git a/temp/stateFiles/1593948455304_NC b/temp/stateFiles/1593948455304_NC new file mode 100644 index 0000000..0cea88a Binary files /dev/null and b/temp/stateFiles/1593948455304_NC differ diff --git a/temp/stateFiles/1593948485794_NC b/temp/stateFiles/1593948485794_NC new file mode 100644 index 0000000..9f7b5fd Binary files /dev/null and b/temp/stateFiles/1593948485794_NC differ diff --git a/temp/stateFiles/1593949281596_NC b/temp/stateFiles/1593949281596_NC new file mode 100644 index 0000000..2f5c3c0 Binary files /dev/null and b/temp/stateFiles/1593949281596_NC differ diff --git a/temp/stateFiles/1593949721642_NC b/temp/stateFiles/1593949721642_NC new file mode 100644 index 0000000..c42d595 Binary files /dev/null and b/temp/stateFiles/1593949721642_NC differ diff --git a/temp/stateFiles/1593949893442_NC b/temp/stateFiles/1593949893442_NC new file mode 100644 index 0000000..47f268d Binary files /dev/null and b/temp/stateFiles/1593949893442_NC differ diff --git a/temp/stateFiles/1593949998893_NC b/temp/stateFiles/1593949998893_NC new file mode 100644 index 0000000..c0ad64c Binary files /dev/null and b/temp/stateFiles/1593949998893_NC differ diff --git a/temp/stateFiles/1593950152800_NC b/temp/stateFiles/1593950152800_NC new file mode 100644 index 0000000..5831942 Binary files /dev/null and b/temp/stateFiles/1593950152800_NC differ diff --git a/temp/stateFiles/1593950992421_NC b/temp/stateFiles/1593950992421_NC new file mode 100644 index 0000000..f45092a Binary files /dev/null and b/temp/stateFiles/1593950992421_NC differ diff --git a/temp/stateFiles/1593951867738_NC b/temp/stateFiles/1593951867738_NC new file mode 100644 index 0000000..d146bc9 Binary files /dev/null and b/temp/stateFiles/1593951867738_NC differ