From eebbe9d7918611331ad8d06f8a1e9883611ee1a8 Mon Sep 17 00:00:00 2001 From: CaiHQ Date: Sun, 26 Sep 2021 13:01:04 +0800 Subject: [PATCH] initial commit --- .gitignore | 30 +- Dockerfile | 15 + README.md | 6 + build.gradle | 121 ++ .../java/org/bdware/sc/udp/UDPMessage.java | 86 + .../org/bdware/sc/udp/UDPMessageType.java | 7 + src/main/java/org/bdware/sc/udp/UDPNode.java | 20 + .../org/bdware/server/NodeCenterServer.java | 207 ++ .../server/action/DistributeCallback.java | 148 ++ .../server/action/RequestAllExecutor.java | 172 ++ .../server/action/RequestOnceExecutor.java | 56 + .../server/action/ResponseOnceExecutor.java | 82 + .../org/bdware/server/irp/LocalLHSProxy.java | 123 ++ .../bdware/server/irp/RegisterHandler.java | 246 +++ .../bdware/server/irp/StatisticsByDay.java | 11 + .../org/bdware/server/irp/ViewHandler.java | 257 +++ .../org/bdware/server/nodecenter/CMNode.java | 146 ++ .../server/nodecenter/ContractExecutor.java | 7 + .../nodecenter/ElectMasterTimeRecorder.java | 15 + .../bdware/server/nodecenter/LogActions.java | 186 ++ .../server/nodecenter/MasterActions.java | 289 +++ .../server/nodecenter/MetaIndexAction.java | 553 +++++ .../nodecenter/MultiPointContractInfo.java | 19 + .../server/nodecenter/NCClientActions.java | 86 + .../server/nodecenter/NCClientHandler.java | 114 ++ .../server/nodecenter/NCElectMasterUtil.java | 210 ++ .../server/nodecenter/NCHttpHandler.java | 330 +++ .../server/nodecenter/NCManagerAction.java | 473 +++++ .../bdware/server/nodecenter/NCTables.java | 21 + .../bdware/server/nodecenter/NCUDPRunner.java | 122 ++ .../bdware/server/nodecenter/NameAndID.java | 5 + .../server/nodecenter/NodeCenterActions.java | 1783 +++++++++++++++++ .../nodecenter/NodeCenterFrameHandler.java | 168 ++ .../nodecenter/NodeCenterWSFrameHandler.java | 171 ++ .../server/nodecenter/OtherNCProxy.java | 287 +++ .../server/nodecenter/TracingAction.java | 61 + .../bdware/server/nodecenter/UnitActions.java | 407 ++++ .../nodecenter/searchresult/ContractMeta.java | 104 + .../nodecenter/searchresult/ResultModel.java | 42 + src/main/resources/log4j.properties | 8 + src/main/resources/log4j2.properties | 15 + .../resources/org/bdware/server/stopwords.txt | 63 + .../java/org/bdware/bdserver/IRPTest.java | 127 ++ .../org/bdware/bdserver/PermissionHelper.java | 82 + .../java/org/bdware/bdserver/TestServer.java | 31 + .../org/bdware/bdserver/test/TestADSP.java | 192 ++ .../org/bdware/bdserver/test/TestSearch.java | 84 + .../bdserver/testLucene/TestIndexManager.txt | 246 +++ .../testLucene/WordSegmentationTest.java | 12 + ...UseShading_2021-07-05-22_37_45_Auto.ypk_NC | Bin 0 -> 826 bytes ...UseShading_2021-07-05-23_29_21_Auto.ypk_NC | Bin 0 -> 768 bytes temp/Counter_2020-12-04-14:39:45_Auto.ypk_NC | Bin 0 -> 564 bytes temp/Hello_2021-06-05-21:30:43_Auto.ypk_NC | Bin 0 -> 842 bytes temp/Script_749432603.ypk_NC | Bin 0 -> 439 bytes temp/TEMP_2021-06-07-16_28_26_Auto.ypk_NC | Bin 0 -> 373 bytes temp/stateFiles/1593916180187_NC | Bin 0 -> 1251 bytes temp/stateFiles/1593916214495_NC | Bin 0 -> 1251 bytes temp/stateFiles/1593924239336_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593924239381_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593924239413_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593925036315_NC | Bin 0 -> 1717 bytes temp/stateFiles/1593925036321_NC | Bin 0 -> 1717 bytes temp/stateFiles/1593925036323_NC | Bin 0 -> 1717 bytes temp/stateFiles/1593941646859_NC | Bin 0 -> 1251 bytes temp/stateFiles/1593942529107_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593947978273_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593948182712_NC | Bin 0 -> 1721 bytes temp/stateFiles/1593948382831_NC | Bin 0 -> 1250 bytes temp/stateFiles/1593948401893_NC | Bin 0 -> 1250 bytes temp/stateFiles/1593948455304_NC | Bin 0 -> 1718 bytes temp/stateFiles/1593948485794_NC | Bin 0 -> 1250 bytes temp/stateFiles/1593949281596_NC | Bin 0 -> 1720 bytes temp/stateFiles/1593949721642_NC | Bin 0 -> 1715 bytes temp/stateFiles/1593949893442_NC | Bin 0 -> 1718 bytes temp/stateFiles/1593949998893_NC | Bin 0 -> 1719 bytes temp/stateFiles/1593950152800_NC | Bin 0 -> 1719 bytes temp/stateFiles/1593950992421_NC | Bin 0 -> 1251 bytes temp/stateFiles/1593951867738_NC | Bin 0 -> 1720 bytes 78 files changed, 8028 insertions(+), 18 deletions(-) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 build.gradle create mode 100644 src/main/java/org/bdware/sc/udp/UDPMessage.java create mode 100644 src/main/java/org/bdware/sc/udp/UDPMessageType.java create mode 100644 src/main/java/org/bdware/sc/udp/UDPNode.java create mode 100644 src/main/java/org/bdware/server/NodeCenterServer.java create mode 100644 src/main/java/org/bdware/server/action/DistributeCallback.java create mode 100644 src/main/java/org/bdware/server/action/RequestAllExecutor.java create mode 100644 src/main/java/org/bdware/server/action/RequestOnceExecutor.java create mode 100644 src/main/java/org/bdware/server/action/ResponseOnceExecutor.java create mode 100644 src/main/java/org/bdware/server/irp/LocalLHSProxy.java create mode 100644 src/main/java/org/bdware/server/irp/RegisterHandler.java create mode 100644 src/main/java/org/bdware/server/irp/StatisticsByDay.java create mode 100644 src/main/java/org/bdware/server/irp/ViewHandler.java create mode 100644 src/main/java/org/bdware/server/nodecenter/CMNode.java create mode 100644 src/main/java/org/bdware/server/nodecenter/ContractExecutor.java create mode 100644 src/main/java/org/bdware/server/nodecenter/ElectMasterTimeRecorder.java create mode 100644 src/main/java/org/bdware/server/nodecenter/LogActions.java create mode 100644 src/main/java/org/bdware/server/nodecenter/MasterActions.java create mode 100644 src/main/java/org/bdware/server/nodecenter/MetaIndexAction.java create mode 100644 src/main/java/org/bdware/server/nodecenter/MultiPointContractInfo.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCClientActions.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCClientHandler.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCElectMasterUtil.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCHttpHandler.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCManagerAction.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCTables.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NCUDPRunner.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NameAndID.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NodeCenterActions.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NodeCenterFrameHandler.java create mode 100644 src/main/java/org/bdware/server/nodecenter/NodeCenterWSFrameHandler.java create mode 100644 src/main/java/org/bdware/server/nodecenter/OtherNCProxy.java create mode 100644 src/main/java/org/bdware/server/nodecenter/TracingAction.java create mode 100644 src/main/java/org/bdware/server/nodecenter/UnitActions.java create mode 100644 src/main/java/org/bdware/server/nodecenter/searchresult/ContractMeta.java create mode 100644 src/main/java/org/bdware/server/nodecenter/searchresult/ResultModel.java create mode 100644 src/main/resources/log4j.properties create mode 100644 src/main/resources/log4j2.properties create mode 100644 src/main/resources/org/bdware/server/stopwords.txt create mode 100644 src/test/java/org/bdware/bdserver/IRPTest.java create mode 100644 src/test/java/org/bdware/bdserver/PermissionHelper.java create mode 100644 src/test/java/org/bdware/bdserver/TestServer.java create mode 100644 src/test/java/org/bdware/bdserver/test/TestADSP.java create mode 100644 src/test/java/org/bdware/bdserver/test/TestSearch.java create mode 100644 src/test/java/org/bdware/bdserver/testLucene/TestIndexManager.txt create mode 100644 src/test/java/org/bdware/bdserver/testLucene/WordSegmentationTest.java create mode 100644 temp/CoinUseShading_2021-07-05-22_37_45_Auto.ypk_NC create mode 100644 temp/CoinUseShading_2021-07-05-23_29_21_Auto.ypk_NC create mode 100644 temp/Counter_2020-12-04-14:39:45_Auto.ypk_NC create mode 100644 temp/Hello_2021-06-05-21:30:43_Auto.ypk_NC create mode 100644 temp/Script_749432603.ypk_NC create mode 100644 temp/TEMP_2021-06-07-16_28_26_Auto.ypk_NC create mode 100644 temp/stateFiles/1593916180187_NC create mode 100644 temp/stateFiles/1593916214495_NC create mode 100644 temp/stateFiles/1593924239336_NC create mode 100644 temp/stateFiles/1593924239381_NC create mode 100644 temp/stateFiles/1593924239413_NC create mode 100644 temp/stateFiles/1593925036315_NC create mode 100644 temp/stateFiles/1593925036321_NC create mode 100644 temp/stateFiles/1593925036323_NC create mode 100644 temp/stateFiles/1593941646859_NC create mode 100644 temp/stateFiles/1593942529107_NC create mode 100644 temp/stateFiles/1593947978273_NC create mode 100644 temp/stateFiles/1593948182712_NC create mode 100644 temp/stateFiles/1593948382831_NC create mode 100644 temp/stateFiles/1593948401893_NC create mode 100644 temp/stateFiles/1593948455304_NC create mode 100644 temp/stateFiles/1593948485794_NC create mode 100644 temp/stateFiles/1593949281596_NC create mode 100644 temp/stateFiles/1593949721642_NC create mode 100644 temp/stateFiles/1593949893442_NC create mode 100644 temp/stateFiles/1593949998893_NC create mode 100644 temp/stateFiles/1593950152800_NC create mode 100644 temp/stateFiles/1593950992421_NC create mode 100644 temp/stateFiles/1593951867738_NC 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 0000000000000000000000000000000000000000..4c929043d8cd1d4613bb8bd7968641541a8cfe90 GIT binary patch literal 826 zcmWIWW@Zs#;Nak3*tX?q5Cakr2D0^?^E2~8i&KL$5>qns()B8{ihDzD=Up)nsC^&q z^EW_MK&c{iD%Tbd4+Y1oIr0LJ4!-sgTfJ`Y*VLC>Z}%r3-)V9Cw32F|jh*p4%k*PD zo~t!CZwQIGC$Xmfa@*;%_l+B_d8X~0wBSjQ^41wo5AuA7TwyAzn(AYj$r~c@w%v8t z+J7Eb>ii$?bxsa!zw0bD-R!K_-9;udmsT}zYmgP?`)ntZuV`)cDxKw@ltO=zB*TYY zT?doA+TV2_df<87z3$;xe!HBh+k!vI)$|_YwBE4jqLps{V?NjV72BI`>o|29|KN;% zwfAK8rhDsmZ9cispI4#&(E)3{y#_Pep9$M5?|gohUu$N-x}6(irr6yHzY`z0ZnKTf zx;s$?&wRIj72C0+;fS%Qj{J|8rxo`Jn6ODrk&&Ks>80_$SIsNWTf{Zt#rE z*RC!-oG=Z6CamJy z=dFGg6cBLA_sp5#O+iKH&Y$7d_2WI~;dAng_x06hw89>(Vg(w<&fz$%WR5aWvmy`& zcr!AIFdz~tvelr3iVCnKTXX}E4F|ap0X%>{LUnI|H!B-Ro(TwRf%I~q3I+xMo?k~% literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f7ce5eaf8303e810722c800ca5d7ae27bf5d1c95 GIT binary patch literal 768 zcmWIWW@Zs#;Nak3Shf3U5Cakr2D0^?^E2~8i&KL$5>qns()B8{ihDzR`!5>^?0vsp zqQ*zotMoK7a2 zRv$0pzny1h{q+jNsd}D0p(4GSN+$eo4t&%!`nT!lx2<*suQ%L2cSpVSanxPyIseio z{_no=^}$`%JVU1^Dz`fC7N6<6)AV4=+is;E&vho-L^X;Wy;Maer3*{-f2?eqZWFb$ z%1AZ&$)dTuELnkChrYeMa)$fCSsP>VTl@19CY9vad)hm#zMdbicl5pMs->pepI=|% z{o1zcLE+u_3;pjJT@FsWJ|$G=c#~?ncJ4RJ$xn^;E%v{bvG?&KwNG9fxYYK4I(5m& zI{#bGLeC{VYv(?<+ab<-n?>=0WfEsLD&hyv(V+!zQ=TOhS=m7HOh8x*q;r8P7#IK~WH;jg literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b7451cf51274b074d9f806e7bb771ec805b4838f GIT binary patch literal 564 zcmWIWW@Zs#;Nak3_*T*q$bbZRfNXu|{L;LV)FQpgtm4wXja-Kuc-p?(9WA^ptNTc= zsi4{W_&$eO$AUG)J1sRl?%xgL-_q*vqKg*{jpXR7~{3OS|$HYbGwf2)GOe@q1 z{JcJ`>Dl?DCHq1Fm(zkL4vXSD4WCb}mMGt`tpE4nO|z#je0gqB{KwvU9mTVg%!*?F zPtH_2GkdwKzQUAK=hzNtmwpMjt@H3$)7SZni@!1jc(Ze8EV#)u5$Mp5KpcS6D|{fY z=dFGg6cBLA_sp5#O+iKH^v`O0>RwuXMl0+RV^?!ZQ1Rz(t;V98 zGc7Hz&8(cM>J_sUXgE8^Ri5dBhCriC@LI+Olr5;#%StTb{*acMd;sJyb`HLyr>}DW zWyHXG8JR>F5TTCj2~en`0^~Rd@J7{%>>`jS5#ThCiRxu^y~r*Cxfua07-2pL@MZ-| RF*7hTtO6=10m?8i006_2r5^wQ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b93ce052d5d0aa6b32ea3ee191a8907588f7ac2 GIT binary patch literal 842 zcmWIWW@Zs#;Nak3xVrji5Can603GpGHw$#4gyi$7Jw{qM0dmm@MewIEvd{4x_AFjVwEsxcn=RfoR zn&d-|+tl>*Qd`AqZ_oec&#cAzszqz=w$C@8t<=AkUum|Oe}z+|qOz&F`uy(G?{2PH zmVQ~>=JosfjrGhPPLU5Y>xBcJe^%W+?+x=VWkp5B)fV>s+$&x@ox|24;~}s}Ea~WG zQSSL?@Ad~ouZ^1$6*DV%QQp#b0Wm>SW=>xh*L~r0(*G&TH$S(%Z~OD+9!m~euctp< zEj7IaQWeQncB&E$jorxj9o1No#?)u3;fT6LPkpalZ z8(to%IXU@ym088DeFwP?Iqp$( z?mR>L(u&php6un_pmZ;KmQ|l)BHw1Cow2jBl8?qs`tW_4>;8t|ev`0S{0@8EI2+8x ztRCF|yfCB0?wx+uCw9x4{??yopSh#cmil!yut=@MNZ svWHOh834Vg504*oeaLPBc^3h=nb18S;LXYgl4l0OH9)!psDgn307pkJX#fBK literal 0 HcmV?d00001 diff --git a/temp/Script_749432603.ypk_NC b/temp/Script_749432603.ypk_NC new file mode 100644 index 0000000000000000000000000000000000000000..d616f730445cf001a758a9c18f3c6adf7fb246c3 GIT binary patch literal 439 zcmWIWW@Zs#;Nak32rtqOVn70XK(>BvVqRuiYH^8PR&jpb>abJ3XU+s~3gYt9JEf!1 zX>PvJ#L&#h*x=ggGg@J*SQ!Gm**R|dPhe66YLy4#0Gx)4f(#E%F3Kz@i8nW~G%+?Z zGceYx%qlKDcaZCl0T0`Q>iJ6U?ui}YH%~U758S+iQ+48mCWYGXQ+rGE68M(>vHu$+ zV#b@%FzJTJe!f#S!i)E|IR02B(&OjYp7!$EPWQ@|8%iYw`*u0sdVl%NGws@Yi?+S| ze2zIb^8NPjUel&zZx`7)=iU6OpQ*2x*RZSRDSu2ze)_#?|Eo$SkSG2{NG)yxI#GrZ zZzO zeD(h2&YGI4>ZYo;TTiS~*}CJ1hv#H%OZ)XpGz@pTC?2_LzF?NOqvF;@3j0^MD6Tcp zmt5khXuH)#IZ#+-@q&OUQ9)5tmM@sHV#(YLUXhhgS3P9{yDIE=j_?D-}!X3 z(l7s@lArCcbbjVud2a3f<7hBRjM$>=EpN6EFyfBSOkp7}(r$ zrIZ#9JpvZR!sQ_rM1V_x<3LWvs0UG8^F+Il5zy)Bs^XzG^l{Po-KAAbMm61usm zi<-z_Q*)DK19VRvWRM>uQE-}m_6U`q+pq5G-||=*(`A)>xi+yCdXG@MhxM1N5u36# zl@(C>P(lfIZNaEQ@6Y(~wNOU6cIps9U2NN$=m*A_syS7@M zY5<@A)hFQyq98XAjlGbuA0f~rNJ^I-fVtywy`rPeasLtO{25Q<8h%5DlckA z+EOa5R%edhKHK|xI5jYA7&b={RJgh7-NZl4H=a}`ldA0`-cK$}CM|p0w1Um7)+@%D zm?^~IS%q5$p2X`JNChQsvYYE3qA_mf;wMF1(#cK7gYpvAprBLib!F_10*#-qzgzYT z4RpWQ+Zju&+cDg`>?)O==&m$M;&OhII>?hE^T+BCb8@xjXT@4(#m%h7BYAD)qycx& z@jxnB;@{$)i@&3oV%;h5Y+S1r*Q&+cND424T3p`4GokFOh*P5@7+*pfcIMMzx1__p zYOV<4eRWV?PNs0;iI|9~-ZV>8GM$a!`DwXx(%LLyUzIwTNsL_Cr1Xt2*A27myJh70 z6Oy)SCA3z+ixr=b9cT$&E+3tZ!|HMB+H6wTrJQ*C>e!1^M>LRCzZ4qFBNi(U;SE!Y zw)L^=yw*iNuRd%x&H{>Ggj3p}c+y_-bV13J2F=s9nx_lsYmuv;Ht37C*B4z-U$jAA zw5`6_m0kCLb-_sk7p$Q{vw0nQJ{Qbcj0Prz`&HMh+02+Z&ZtZXY;M;4JM_Q+#>=j_?D-}!X3 z(l7s@?pLdOu3a9UrU;=WecV&%?FYnWOxQLfjzY^@5JibXKVi-ou`y*J zII~=WZRT_7TG;Yj5{8~1xGo3G0>QW!$bfi0bGRKiJailZb|_qs%md)y$dI^>l#T}; z;Sx*NVfvtwJDda|x3C`w3&0IMOnl*5#0o`-6}q91M|Nmi*dy2jCtwf|M}(BIFtEAh zN+~TIdIT(rh08-Mhya%W$APpg8c3H0y4?3l+Il5zy)Bs^XzG@4|MlDNAAbMm61usm zi<-z_Q*)DK19Vp%WRM>uQE-}m_6U`qTd(fu-||=*(`A)>xi+yCdXG@MhxM1N5u36# zl@(C>P(lfIZNaEQ@6Y(~wNOU6cIps9U2NN$=m*A_syS7@M zY5<@A)hFQyq98XAjlGbuA0f~rNJ^I-fVtywy`rPeasLtO{25Q<8h%5DlckA z+EOa5R%edhKHK|xI5jYA7&b={RJgh7-NZl4H=a}`ldA0`-cK$}CM|p0w1Um7)+@%D zm?^~IS%q5$p2X`JNChQsvYYE3qA_mf;wMF1(#cK7gYpvAprBLib!F_10*#-qzgzYT z4RpWQ+Zju&+cDg`>?)O==&m$M;&OhII>?hE^T+BCb8@xjXT@4(#m%h7BYAD)qycx& z@jxnB;@{$)i@&3oV%;h5Y+S1r*Q&+cND424T3n*9-I-YSRm`c;5sa@P4LkGsuv_xs zUNu()@xD4J(=?S6Pt-(A`KDQ-lId&&&ri#plh$St`>GVeOk(89E~W2;x$c-{<1Hi4 zpOCbbE1|UtUaa_h>_BVqa{cIR99EB07iW{iF6G4ASI1tYI--HB`nAwl9+x$f z@XOi!#qxUdZsM0;xmB6vmpAN;cEv+%_5g2kL8|-i!!fe9t2U2^fsx*!Sbai&Owa zq3?y!C?RkZhe3qliVGsyRk5pgxHSAtR9>E_@0{$n#=VSEz?!oT!4RTJv{x@A|rT{Rg}{RQ+;L1>bL$`k>KsykWk;p zj6y2Gd#bd)r@rAjq{v^F-Ry2!sjg2dS)M3QU{-V> zYUaq4eIO)Hv&THjbcK|hao@{CDL0FwQ>e^iQ?+*wHni0|B+djc z?`68J%#5y-yv1(xRNL!Q%?Dg^&6@{j=2vrnFHMwVK}ApRvdYl=YJc}slf6|Vyyu@M z%NJ@dPm5fykqPZ=d2}#tufTV#ATTR9Ju+w>YdLGowgPfT-Q`5PCzgZg6VQFB{ z-YeLKef#3Tpxf#|-!f!VFP8No&Av7XZ(AR~o~HxuoqB5F9l8S#I}#5q$U|4=Q3t-d zNFQ1FLhgK_j(i~tU&xg&|J*J2f4K-eOD}xO?(Da1&+WbNX}4bZ#4;pxJ*(BOcc-Vd z<~iB6hb^wB>N%lve}kZ0JTpxXl~xN{syR9e*?>9OVHfsE&Xk|Y-n=YwwJ+r z9^A}>o!7~@zO83nP6^nigutSNz+DMJM0)zI2wn;Ni1 z4cJ``xT6}dMGe?h4Y)}$cBi;@zg5q>9LL1QF}84w-8se`ImQ-_u`9=<$uV)~*tl=4 zXI+kBVxyQ?C?@U{la3S<3&q5hV$!6TxKrGI|6dJ0H3)a*fYuXWOYQdViVojS^Q+KReo=4TQFUNFfW13o+rtVDB zj!aVv)6|veu*r1j&h**mdiB)H0Ug?a4lO{3?m&kffetM|hps?}O`yXLR~xnGqxHn@ zEQhSwWM*SIw6GkyvmAD0Ikd1GIRDvXuHAjYI#^w6U&8vEK*>uQeaIfFn1{|p8MBxx~rh=HXnL688C|sn7a&EM;S1S44A77)NC3Z zHfDHkix;m@&SYJVVd&W)hMt9DHKcQwRg!LPw!Q{ZnVcKlMA?|8!{zt%ozyhxs{VQP zx&$BB|93&Z-0zz&_rdJzCIH7JxN-W{TmM%=|9(C>GM!@yKG+k_Cqkb+GIhH;Rvi%P O&;J06E*8xtApig@M_B#< literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593924239381_NC b/temp/stateFiles/1593924239381_NC new file mode 100644 index 0000000000000000000000000000000000000000..04f2096aa5f4231ad8f35e0476b3c96ec7d01523 GIT binary patch literal 1720 zcmV;p21ofHiwFP!000000HvB+bKEo($JK`3LZ=Mn@(5*z;fZc6*^;dEp)}csE+kD! z3Nw8I+14g*9eZnQ+hmw_7(Negd;*3Co_S@4kH8Ec0V#W_99u-5O(vP_ImbswN58*( z%P;?cOH~PaMS0MVl9#*`{VMKH@>Ev+%_5g2kL8|-i!!fe9t2U2^fsx*!Sbai&Owa zq3?y!C?RkZhe3qliVGsyRk5pgxHSAtR9>E_@0{$n#=VSEz?!oT!4RTJv{x@A|rT{Rg}{RQ+;L1>bL$`k>KsykWk;p zj6y2Gd#bd)r@rAjq{v^FD>R?S&?;OB2#gWa8(6&coT zksOF}=D2Off0-2#*H+&!4)a7D3q2??tce(rrJ zsAJ3&d_2xh<`MTM@^Bu7%uIVl{Hz*m%qM6jcp&9?ZfAyl=Vbqz?U}>(498ZQiMicu z$WD2eQeB@^vOH0qz^v#% z$Q_>MTcz4+w9hUFBs z#m=iso!ylPCi*<06St4Y9J>HvV=3QEcX8}6myLx)^*pGV2RSowrlxZNf3q~C!_vT@ zy;rae`}W0wLATX`zGcX!UM%ZFntg2&-nKq|Jx>SRJN4AUJ9Gygb|fBJkcY0!qYiv^ zkv_8Uh1~f<9r;2QzK|Oj{S8pJ{sxxImY})MTl(8_+0f*)Z7+lM zJh+($JFk;*eOu4EoD#5234uikfx8ldj!FnDN(fw)fSXFd?na7Q&@iyE-28gP?h>`rm*eyg5$IgW{qV{G9VyK{^?a*Qn;V^@wzlVjq}v2ov8 z&$=AN#6~f(P)yt@CLJjz7K({0#iU6wai_Tb{=YuJayU~P&ct^6=#De#h%>R^Ok8oM zO`NGa&MnVT>e-jWn%c0Y7Obf|*0dwm)Pgm2#hNy;rtVm`JddhpUykX}#x%7sP2HKM z9hs&Url~8_VUy|5o$0gB_3EjY13I(;9a?}6-GL4}0v%d_4qbr`n?Q#ht~P4VN9&2* zSq@pV$;`%bXkj^YXF2T1a%f>WbY__~o7{XT(RPF5)$*{WCYZY>X3wkZ8JCj+vq^zj zq`=&zz&c8SS){;RrNEj}VD3^_Jom5XbXP&!Z9epDGGG=NFn1ZSjxt~t88BBFsM$0+ zY|QZ77B60*oXNTz!_c!q3_T0QYDniUt0djpY<&%+GC4Q8iLx{XRsHko zbqPMM|L=l+x!*Tm?t|IaO#qHdaO3o?xBjn${{4J%WID$Ze6S~;PlP^uWa@TxtU4gn OpZ@`-70OyAApihAZewNu literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593924239413_NC b/temp/stateFiles/1593924239413_NC new file mode 100644 index 0000000000000000000000000000000000000000..9c37fd7e5611134b0036aadad992f22dd5c799c6 GIT binary patch literal 1720 zcmV;p21ofHiwFP!000000HvB+bKEo($JK`3LZ=Mn@(5)g;Dv51*^;dEp)}csE+kD! z3Nw8I+14g*9eZnQ+hmw_7(Negd;*3Co_S@4kH8Ec0V#W_99u-5O(vP_ImbswN58*( z%P;?cOH~PaMS0MVl9#*`{VMKH@>Ev+%_5g2kL8|-i!!fe9t2U2^fsx*!Sbai&Owa zq3?y!C?RkZheD>R?S&?;OB2#gWa8(6&coT zksOF}=D2Off0-2#*H+&!4)a7D3q2??tce(rrJ zsAJ3&d_2xh<`MTM@^Bu7%uIVl{Hz*m%qM6jcp&9?ZfAyl=Vbqz?U}>(498ZQiMicu z$WD2eQeB@^vOH0qz^v#% z$Q_>MTcz4+w9hUFBs z#m=iso!ylPCi*<06St4Y9J>HvV=3QEcX8}6myLx)^*pGV2RSowrlxZNf3q~C!_vT@ zy;rae`}W0wLATX`zGcX!UM%ZFntg2&-nKq|Jx>SRJN4AUJ9Gygb|fBJkcY0!qYiv^ zkv_8Uh1~f<9r;2QzK|Oj{S8pJ{sxxImY})MTl(8_+0f*)Z7+lM zJh+($JFk;*eOu4EoD#5234uikfx8ldj!FnDN(fw)fSXFd?na7Q&@iyE-28gP?h>`rm*eyg5$IgW{qV{G9VyK{^?a*Qn;V^@wzlVjq}v2ov8 z&$=AN#6~f(P)yt@CLJjz7K({0#iU6wai_Tb{=YuJayU~P&ct^6=#De#h%>R^Ok8oM zO`NGa&MnVT>e-jWn%c0Y7Obf|*0dwm)Pgm2#hNy;rtVm`JddhpUykX}#x%7sP2HKM z9hs&Url~8_VUy|5o$0gB_3EjY13I(;9a?}6-GL4}0v%d_4qbr`n?Q#ht~P4VN9&2* zSq@pV$;`%bXkj^YXF2T1a%f>WbY__~o7{XT(RPF5)$*{WCYZY>X3wkZ8JCj+vq^zj zq`=&zz&c8SS){;RrNEj}VD3^_Jom5XbXP&!Z9epDGGG=NFn1ZSjxt~t88BBFsM$0+ zY|QZ77B60*oXNTz!_c!q3_T0QYDniUt0djpY<&%+GC4Q8iLx{XRsHko zbqPMM|L=l+x!*Tm?t|IaO#qHdaO3o?xBjn${{4J%WID$Ze6S~;PlP^uWa@TxtU4gn OpZ@`Sei3jbApih6k6_jS literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593925036315_NC b/temp/stateFiles/1593925036315_NC new file mode 100644 index 0000000000000000000000000000000000000000..bce08187b56555ad9b3c3d7f4c2dc2b8c349d364 GIT binary patch literal 1717 zcmV;m21@xKiwFP!000000HvB+bKEo(#|5D+ES)lx%OjM1;E8T5Ti5bH+iXJ@5(*74 z!xPB1HgW6NTU*;E!?eTjd3fU!Fg)HlqX7~t5*-PcxBJ%8JHnV%q(UElY`^&fe zDPZ>27?K>n3S_j z))O$uMXm0f$OHLk4-ArJi^*7lt9uW`W3io!;&A&wRatTP@gBImiught%SYfDcxew@ zS=nau_;Y!>w7!}ZkIRR0GG2b)dboC7I+n+=tZD_`SoAShS-!n5X1arm+E-Sq$XC?q zOe*lfo}S+JVtU(kvK>oNY_DebwGR3CWCj4(((lU(46Y-NQG_vL3S9m|6onr9Zl%K& z_&Gp`NF=F51YsHRDU1mT(<6B%JH;uK;QB_U1(XHqZvo2GapQ@qh#Q6K37Ubytv zZ@<6$!=GE=(?Qnk?w{< zs=!+?q@zIp!5ff<(G6A2rHKh)2m?-W6e{p`BL>}=ioDiA0|-qmC=FUM;UeZly*K!A zaP8W#j33BE)x#Ut%vWs))*tnkX(scuwk+EAohXvLoTuODIcpF6=(%frZ+~eGE$g04 z4`sD6BR3R8F%(ep!_>?O>Ke$0l7-L7s{>#EOdRh?=NvPX0% z)xOAz-AeB@#%H%OY~Po4Ij<7q!*;tuO6}82Ss4#$X57(3IMh4L?Nh5;VmB9s$WQC6 zZbp#Kh;de{tTa6XDv~y<>DAMT-(#d?1$e5BOZJo24N6j7&P;e z^Jb>9V|lhIm=)}v1M!>#JFl-?7KiG{ELa2P1^m#~aG>+!oGWPAnLa|FmiO+){A3(NV~3W!n@WdYUb&Id#{>0c!&PL!=A)L2lCLD`LG9H zeWV{c_zL~`3VZSuI`|5G`HIfna{reL6gX-TId|Uwx6|v<0>Ic}IOaP8-^scI{=b znFqJ?VDELZ&TpGpH&X(3DFHc@fc%w!dMW`qlz@DdfZIyI{z_TE;V3> z8nC|_a8EU0hZ?Z28gQFp>`!sydaIdtGmeRiW9;A<`*VzYa*Q1uV_%L*n`7e7v31?r z%(@xH#6>Z2P)z(OCOs)84vL8{#iUI!@u#@^`oG!1W;jz9&ct>3=#Mk$i8FEFOnh;s zZJen;&K>trn%OtQn!2#24y>s^*0d+q)PXhi#hSLUrv6xW+>dHz-;8PIVwyUbrv6OR zo=j5*)6|z~)@GXdGyURyy=LmofMzbBnFDC%4>aouG;;vWe1T?dpjnTzjmG`aW@3Mq z8E+SvyI5upmYF}xtS8IN!7}q^nYWAFzA4dlffF?H@U|wnzb2OVtD6}&lLB{1fjgwY z{iVQrN`X71zB$EV4m)e?5F!+`ML_gNd>N)J@qy}OBmfL=Et^krocP*<)gVYM~_@zuFiA?r2hCH LzL_kECLsU-Wny1* literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593925036321_NC b/temp/stateFiles/1593925036321_NC new file mode 100644 index 0000000000000000000000000000000000000000..bce08187b56555ad9b3c3d7f4c2dc2b8c349d364 GIT binary patch literal 1717 zcmV;m21@xKiwFP!000000HvB+bKEo(#|5D+ES)lx%OjM1;E8T5Ti5bH+iXJ@5(*74 z!xPB1HgW6NTU*;E!?eTjd3fU!Fg)HlqX7~t5*-PcxBJ%8JHnV%q(UElY`^&fe zDPZ>27?K>n3S_j z))O$uMXm0f$OHLk4-ArJi^*7lt9uW`W3io!;&A&wRatTP@gBImiught%SYfDcxew@ zS=nau_;Y!>w7!}ZkIRR0GG2b)dboC7I+n+=tZD_`SoAShS-!n5X1arm+E-Sq$XC?q zOe*lfo}S+JVtU(kvK>oNY_DebwGR3CWCj4(((lU(46Y-NQG_vL3S9m|6onr9Zl%K& z_&Gp`NF=F51YsHRDU1mT(<6B%JH;uK;QB_U1(XHqZvo2GapQ@qh#Q6K37Ubytv zZ@<6$!=GE=(?Qnk?w{< zs=!+?q@zIp!5ff<(G6A2rHKh)2m?-W6e{p`BL>}=ioDiA0|-qmC=FUM;UeZly*K!A zaP8W#j33BE)x#Ut%vWs))*tnkX(scuwk+EAohXvLoTuODIcpF6=(%frZ+~eGE$g04 z4`sD6BR3R8F%(ep!_>?O>Ke$0l7-L7s{>#EOdRh?=NvPX0% z)xOAz-AeB@#%H%OY~Po4Ij<7q!*;tuO6}82Ss4#$X57(3IMh4L?Nh5;VmB9s$WQC6 zZbp#Kh;de{tTa6XDv~y<>DAMT-(#d?1$e5BOZJo24N6j7&P;e z^Jb>9V|lhIm=)}v1M!>#JFl-?7KiG{ELa2P1^m#~aG>+!oGWPAnLa|FmiO+){A3(NV~3W!n@WdYUb&Id#{>0c!&PL!=A)L2lCLD`LG9H zeWV{c_zL~`3VZSuI`|5G`HIfna{reL6gX-TId|Uwx6|v<0>Ic}IOaP8-^scI{=b znFqJ?VDELZ&TpGpH&X(3DFHc@fc%w!dMW`qlz@DdfZIyI{z_TE;V3> z8nC|_a8EU0hZ?Z28gQFp>`!sydaIdtGmeRiW9;A<`*VzYa*Q1uV_%L*n`7e7v31?r z%(@xH#6>Z2P)z(OCOs)84vL8{#iUI!@u#@^`oG!1W;jz9&ct>3=#Mk$i8FEFOnh;s zZJen;&K>trn%OtQn!2#24y>s^*0d+q)PXhi#hSLUrv6xW+>dHz-;8PIVwyUbrv6OR zo=j5*)6|z~)@GXdGyURyy=LmofMzbBnFDC%4>aouG;;vWe1T?dpjnTzjmG`aW@3Mq z8E+SvyI5upmYF}xtS8IN!7}q^nYWAFzA4dlffF?H@U|wnzb2OVtD6}&lLB{1fjgwY z{iVQrN`X71zB$EV4m)e?5F!+`ML_gNd>N)J@qy}OBmfL=Et^krocP*<)gVYM~_@zuFiA?r2hCH LzL_kECLsU-Wny1* literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593925036323_NC b/temp/stateFiles/1593925036323_NC new file mode 100644 index 0000000000000000000000000000000000000000..bce08187b56555ad9b3c3d7f4c2dc2b8c349d364 GIT binary patch literal 1717 zcmV;m21@xKiwFP!000000HvB+bKEo(#|5D+ES)lx%OjM1;E8T5Ti5bH+iXJ@5(*74 z!xPB1HgW6NTU*;E!?eTjd3fU!Fg)HlqX7~t5*-PcxBJ%8JHnV%q(UElY`^&fe zDPZ>27?K>n3S_j z))O$uMXm0f$OHLk4-ArJi^*7lt9uW`W3io!;&A&wRatTP@gBImiught%SYfDcxew@ zS=nau_;Y!>w7!}ZkIRR0GG2b)dboC7I+n+=tZD_`SoAShS-!n5X1arm+E-Sq$XC?q zOe*lfo}S+JVtU(kvK>oNY_DebwGR3CWCj4(((lU(46Y-NQG_vL3S9m|6onr9Zl%K& z_&Gp`NF=F51YsHRDU1mT(<6B%JH;uK;QB_U1(XHqZvo2GapQ@qh#Q6K37Ubytv zZ@<6$!=GE=(?Qnk?w{< zs=!+?q@zIp!5ff<(G6A2rHKh)2m?-W6e{p`BL>}=ioDiA0|-qmC=FUM;UeZly*K!A zaP8W#j33BE)x#Ut%vWs))*tnkX(scuwk+EAohXvLoTuODIcpF6=(%frZ+~eGE$g04 z4`sD6BR3R8F%(ep!_>?O>Ke$0l7-L7s{>#EOdRh?=NvPX0% z)xOAz-AeB@#%H%OY~Po4Ij<7q!*;tuO6}82Ss4#$X57(3IMh4L?Nh5;VmB9s$WQC6 zZbp#Kh;de{tTa6XDv~y<>DAMT-(#d?1$e5BOZJo24N6j7&P;e z^Jb>9V|lhIm=)}v1M!>#JFl-?7KiG{ELa2P1^m#~aG>+!oGWPAnLa|FmiO+){A3(NV~3W!n@WdYUb&Id#{>0c!&PL!=A)L2lCLD`LG9H zeWV{c_zL~`3VZSuI`|5G`HIfna{reL6gX-TId|Uwx6|v<0>Ic}IOaP8-^scI{=b znFqJ?VDELZ&TpGpH&X(3DFHc@fc%w!dMW`qlz@DdfZIyI{z_TE;V3> z8nC|_a8EU0hZ?Z28gQFp>`!sydaIdtGmeRiW9;A<`*VzYa*Q1uV_%L*n`7e7v31?r z%(@xH#6>Z2P)z(OCOs)84vL8{#iUI!@u#@^`oG!1W;jz9&ct>3=#Mk$i8FEFOnh;s zZJen;&K>trn%OtQn!2#24y>s^*0d+q)PXhi#hSLUrv6xW+>dHz-;8PIVwyUbrv6OR zo=j5*)6|z~)@GXdGyURyy=LmofMzbBnFDC%4>aouG;;vWe1T?dpjnTzjmG`aW@3Mq z8E+SvyI5upmYF}xtS8IN!7}q^nYWAFzA4dlffF?H@U|wnzb2OVtD6}&lLB{1fjgwY z{iVQrN`X71zB$EV4m)e?5F!+`ML_gNd>N)J@qy}OBmfL=Et^krocP*<)gVYM~_@zuFiA?r2hCH LzL_kECLsU-Wny1* literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593941646859_NC b/temp/stateFiles/1593941646859_NC new file mode 100644 index 0000000000000000000000000000000000000000..7b8620ccc9b43b61d358584b835e5767f2f317af GIT binary patch literal 1251 zcmV<91RVPxiwFP!000000Hs*Va@<4|?IhSq1}YTcu_erc6`AR$T5=X7v12?iCcy@( zvVvBtHJ(_O6seWiMI}Y?Bm4k6zJOxEnk`j)0#$qh-JX%t6OFK@Jg*v^d;9k7bC0fC z`sE+6Ru-V26_Y_2Jr{)OCd1BD(&^-;Y23@z;MZgWd?N%PgO& zas+ybDD{J5wXdFzK~K&{jJ7nmIesFZiD4qr$#7p6({yrY4Bo0ez7$95DR>Dkjlqo? z%d7AUbuveSEIh6- z^azbPjKhF>fzP>A%KXv@dk{etsEG1dQK^Uqm!JlBWz2YlBcH&)56!#^QwdQNK}sc! z0}sbB4ZMJhAYd$35kY|`1^4{`%Mg*+RQnyv+Q70l*b(J{VQ%f}U%&nS!S{b&0hhN; zO*0W~8)~v*1g`6YLX`(u5*_EC-Uarx{`{I{$}*w^(SFyM?}4c=Sup&PMCN@ZzBIDnjU*zmzKpJRRF=O22vZuPV9iITePZ``tn z27qUO%|Sd>NmM$B!5)iLCRr7IW7cde=#y8bt%tjF6g%ixMH5xb4YytY&yp+@79|ci zN+Wfw?4;a5BNb0m^{B{lV+b6B@s*WUwFGTOwB3CFWb5I``nVxPo+l@@$Gu7))Lz() zw4-%iug)F4d$Rx4cy1ujF>Fs0Rpaie_cD1{Zr!g*i(I>}y{Tw3;baSgj!ZPttv zwor)CqZ;=dJWDqVCA6}*%Wi2}h{33vi_EIj(n(#%1A7V^kl{J@qBi!Yk-^W_-?#0; z0R7MRAD&9A-*epiAy3u8mgMR!&x&-+in>{yj--u|v;NyZ z!y}=E#lMAv0DgxdhNj=a#kgK0uGfgWl~rM)8gYXGTnNR_BTi3`)afZ?V1GFsydxdK zd2=Nyo#+F5I=RA0XJSIGdh;UF*?cwxm!}mhNo%`GrM5a)NDQ6Zq|9}&G!4`CT^o7% zgs7`p5pERl)r!x?j*J9fFCSivLz;1#+H6zUYdO*G)#0mDhqaK^JQZ5Y!&WN~qAgRh zuJz$fUYjCcRv)q$X9dNt!YOW1JnAlayrSe$i{^1x&EpmHwaVF#Tl9sy>kF@_FWjOp z+*M!X+^+k-x}d0q3)0e{$+8Z;oD1Tw#s%RmQ~F)6k*Il*M2)d+J?v})vKSLzj9;&X zUrLrQmNuJr8^842oysD=v}I#NJMTf7n+4k3Ewu4_x{+IqcQNg^W|7O1$8t}^MVVat@zOOu6xUL+DqezsM^xmD0F_CGuw!_D2fXm8Pl=H~9 z*ILZS37AnDzO0?-kN zU>NxfB>+;wl1M-@Qj-EB9&n6t0DO$XXoNV8VB~Y`vk{2`lE5HPU4Ktkudl1uf559l zRl!SF{`&3rkAC=b#k;trdRy>hOO>c7hu$@LSc>Yf$dcpn7k54VbNS^}^}qhCoK}rc zgfOFkO7DF&pT4jDfptJ3T9@TS=oqL<;xmeoTEz#om|%Uxvq}Z60sxs$DfJsMa2j(g zKVJQD_13Lk5j_>Lta|IW^jBr@7r&}6qf}%`WmuH$3D4uKm?Yn-H7gJJ{Ecg{yEC&w z!@4b!15wT#x6Sx3vm)Z!iVWj0PsFj%gA&7s=>y5fo6gSQjX_#X4rR5_P^PlIgoESw$e<@ z?PfuCiu|BhjDgH5?(e|4*__qpyy7URHDdT^ZpVhb$Tv&DrOx@o1VKB;7RqC9~) zqXQv#c$#mOYO~Qk^C->so~VjR8EYRlkIRKnKGn)ndq_RwrkcWm+F~|NjA4m+IL~=@ zQl(Wrg0w~q(n_ZJY3r$(BUAQ)kUY&E^C;6Zq~whIUM5PpSsa~0WgeTV-FvX1t>z(d zCU|)-(`995bfx4icB7}-USDcH;F4?Jd@wV=n)`ccq8tk}SLEXV^_=3xiE zx=0^d_=4_y!H#@E3t!NcFLdsf`@dWSzNHq(vOA-;?YX@ch<2+5B9shUKy*oXv zHP6YmJ#2A3RnG~W>l*|u^~IL@2C#jxL38uA)i;3M>Kj-tTY~03Z>ev~WkZwGw!IA0 z^WbJ4?A#~g`nH~RIVE755(0}70(T_@9hDGRln}To0XLO^-IY+gPpId0S3_%!ZEC<4 zHDGr&;Erm*7Byg3HQ*-2*q!3q{Z>8iavT#I$JoL#cIOy(f5ocn-nYiLi zn>bTc^*~Iz8uq`jcICOnz}Ph zJ2FizOjB2;!zR(x^)2XtryICx&s|{1Uj?;9l8P?Hh~U1Ty4~zkJb~r zvmCN!k(rI<(86-)&T`n1<8^sd+kD{LWWX#kVD2(t9c92QGGMMUP^)QF z*jVAYC0@KjIg@ochJkN`82A>7)sW6rR!O>*+4>qtWpXZb6J=wW4wv84cT&@&tNQcm zb?JRl|LX<)dcSYJ-uGrdH$CsT^lqHK_11rv(7&Hgj!frRdLQnI=M$mN9+|pb9jgim O_3M9BlQRS-Apigpm1Xe& literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593947978273_NC b/temp/stateFiles/1593947978273_NC new file mode 100644 index 0000000000000000000000000000000000000000..68c6a3666bc1213e51983656299d604b5f0b0649 GIT binary patch literal 1720 zcmV;p21ofHiwFP!000000HvB+Z`?!_$6Z2ip(>(W9--_5PjurMdv56iZL$qrNSe|V zq&~seo=x03_ST+lvqG&9pNBU-0pfvYUXl0+NPGllvX_oy4;gDyZIwOe_{_{XzyJ7_ zU;gnfR;AY~%ENw?yyT_mS8;!mr?TpA7r88XEFX!uD3hyy{Qcy!pML%KEpL73U5blw zDypHkp7BcFI}r!s`L4Gf&yN@mq<3TYDSyE?GM*o99LO@w4{z^!*A@{E__28Iz4k8b zde;}W@g(|MOy|}sY5t;kCWeFg_qAtB*X3hz9Eq}$-n+9gCNj-7_V`#0a7p=!auNBq zoQ{R`KHXKz+n6nHqlz~o!Sjv9>K?0*+b3hs^VZbon)Kee1sGz01(*TpU33A@*@$_AOymP6bDFp7oha+P)=}y zgiko66a)bJNdV%+XGxgEA{LBBh>3u(P^kI&vEYO=9$~2Fnn)lX@>TSW39aW;D9C}yfQ7NjUB1=xjU)=HZ&!v}F)c^Xkc3L$| z0=0Aki1gkEko0}^4{QPggH2gZgpPp#0-q5Ss1%c{3|Q-4(kfBCEWGD=03RE9;_p71=*ib?XVTC?(i&)>KP`+IXM zG_1QKITYpGam$SVGAkmkt$|@2=7~5FdQfaw6ERA2@vtn$%0Uf>ij5~yEKt-l3heH! z>0p1TW6TtMJkF+zh`SScw1|Rcrbk8mtQu@B7HB58FXeb)XNG-mdhpHe+!6SOV<*kT z!fqC1ugDLJVxUHsesJ57`sWB`z59c}0 zrd3+iBXDcPAgyGYpSGTuIWlD*2+7mzF^@7mLxRt^?`EQu+r{xIROYd%+Pw!`+G-vW zXM&e^GhJ3@MpsJSVn2GS?e(SR11`Dd%?ESytA)RpCd#p-qNn#+W$1l%uz#w_-ntRq z3(k|}3$>f4MXq~fLOXAd4o2-W2pne+nlm^(GHe_iMz?ts8DWK%De{@`X^n}oOZC#dJ?fP1H!T6hQUz=Mv&0}JxNm3i2K zuP)Mu7QUc6U$7%z(83pVv4 zX{~uqw(Vid>#2H9;9TD@Y^g7@)Hj6fiw&Ebx2?V*>{j2Xc^C0Iw8P~VPjue%yrYiv^k zvZw*Ms{wUX1G1 zH0j7Ru`o?snWjyqsXNnWpX=3AuLd-=0ZlDHQ+J?gN1&+%XzB_yZ30a@Ty4~zkJb~r zvrJjD$jrtvwXjUxS*9IXrWTf|Gs~=5(~A_e9u1=f@TbC<&Mxqm&Uy9(NF^MP-Z0kg<}xyyiclmWBIfVs**t)@|7 zV}<9Ic=-zDOxD#H2EGkq;9DqGLpoPkCFxdX>uVsD$+^%?l#OLNTz=2qNzIb3>(8s# zrT0nwuNU;|{l58n-<$v3@Vt}KyMFrCTmM}`|6V>hHl1VXefUT`p9p>S$kgrXSXDr% OU;hIY(8u2=ApiiOx?}JF literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593948182712_NC b/temp/stateFiles/1593948182712_NC new file mode 100644 index 0000000000000000000000000000000000000000..7a7c4b0b1e90fecef436ec7f88154b80e320ee29 GIT binary patch literal 1721 zcmV;q21fZGiwFP!000000HvDCa@<4|hLxDyOez%NvS0yW7Ocov-BRD21&KW|8L;DE z2dc7ymegY_k)?^$lh{QiMe#iBcmj$AYqnJJ2vqS1bbBsoS!&#>acWYYbB<1TpY#2# zn}7MoyHu54uP6`tQSy?PqF=@RNuJ88zggt6-v-ai)m;`z3>8qbax4y1Q;`ze3H*D{_TtnJG(%@6Kud)Ma?5BZUJ?!ERd zY73Dng zEqO8)())B~1{R8WOz+hdL6QN@ufWT)21yFkL*J8r;5zi_Wv-K=g}sj5#ZS)M3Q zXwK+B$Q_>MTcz4;w9hY zo}E-_Rgb`}5rednX@1&zV&=${eIO)Hv&THj^b83;U-b$edd1;edzlL z`1QQ_yp_rHNStm8x`XX&z>aI6=Z%$#{6HS+hSgx6!VgS6j>|$8v(Z@ltZ2)uwkC>H z-KW~T*m?6(=XWK7i9V0$#O>oT$0`8OXyu#bE{+}Mve8Ib&jXuzpfeL^=5#LLZ(4&p zv<8IjwE}I}w>Jlb-MRw;%aBdISo(vTb!`&f)}NrBrvvVtYHHyfxC0M55)UlM16Sr@ z2fn&UA6oc=?tHC{%IoY;{Ev~2PIe~M1!?2~k$Wq@Bwl6kpZr--~hOk?GL(64L*xctW^=-LqXmZ-N zmw|d7)XamN`(#|-*0V0B1Y}b}Xi-Awu7t3o5<-g-LRTfArV^055^DDe^}OzCXsxkL z4alMfQ(U{>s^?vfV{GFXSvW@S9HWjLBMZmKm1ErG7`t<9 z+_%=VE=Mu8QH(7VV|R*iM~bn9V(dyWZc>chDQ>_2uMe;s&cuc@w%tCu4-J4U`<@HCQYn~JJv1Fqw3k0W18BSCKjfN zJJX~i)5O9wab=n|nWpYcpM9=ZPrV$_)CM%Q08QP2rX7K%7NDss(6k9O?QpeGdp=rE z?9MV}%_1`!%hbX$b!VA&WSLr6rp_$0W|5l@CE9Lq{8}E?)C6J>zmxU^Xc* zixil<6j(t`A_L|s1GSn) zg^d-STjIqllrveEV;J~0h=FgRSPki1WtF5`nXRvZR3_&_H&Hg0>2UcyeJ3?dx~e~~ zUYFh{^}k-wulM`r>wRzbbJO#VOYg?%TW|e$3H|%|#O?2)P4)v>C8 PP`~~Mv=vQfCm{d;XxV5! literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593948382831_NC b/temp/stateFiles/1593948382831_NC new file mode 100644 index 0000000000000000000000000000000000000000..772b8367c88d533740444c5053d2d876dd74017e GIT binary patch literal 1250 zcmV<81ReVyiwFP!000000Hs*na@#}{-X^4N3=@X(b4&0ASJd8Lt(0ESHg1T)AteE3 zdIc+K{nIFtBCS$qn06RmgcsnBCt$eXnpyR<`A=D+|jLd*rdcbN1}n?|eF1 z>6d@NR+@ljlnh#~|3W0Pm3pl#9I3RmAB8Fro;s0Ul=$0!{Qd0XAAkLK12nqewim@C znRY=V5UF}HmZ$Qf2O8dNM7N{Bz20+iDcXSu2kldpjKaZ#9(b$p_)?t93-Aiu>VcgC zi!=8Nc{M}tj>606Om;i7^Ubrl>CU-4cV&_)@b+|$OpSu}k%;vKx3w)w3ePvxRV)?w zsHd0Lo-VJQdTm#VuwAU~Ywhx290LG0^|7JAHgOz_KuDMZTZb|TA_abew9g%zk-jbZ z!tGn!axsMvbLv4RC4?@emhCafF(D2Vp4}HhK+p4h+jkvi`3~dEfk<#+b8JhgqxFgG z3(FHopW4)MG4`Y_n1>wKarz8-dOpSpp%6n`fm=|4hdKe~N}u`!Gaq5Z9B%1+Iky;t z!j_gTELTE=h(O#DmSmJ!TzHh*uI)0Yi~UYcS}P~5btuxICT{D_U%&nS!S{b|f{g=R z(n$CRnwco+f^9WSWIBuje;j}M5agfRFYfB!{Mc0B&HKpG8R{$$SKwWQc?;?<+C!R% zJ(XlK_rW?*=x|1`0`D#O(7nD0Qti}02s@B-4og0G>N8e5KmX9!-EBthbLpwHxwo62 zY5>mv>XZIR27YQF8ha)}FNiY#8@*<2L7!ZkI>$#d6dPzy`U9EF4EIg{&w|JmIZ6z$ z8~SoA^GUgZ`m#R?<1{t1VFAHOHF~^mrn%X2WoAl2t0Z)IDhwh57s@bux{z#G9ytEy>kdo)xK?6*aS(jHIQJ)Ark( z#HrD_oLoX0wwKevYtrGY znk$guKn?TD$rMgF6%#Vm8z+&9X0suw^&yLK)=>O9oZsl|_DO#m0!%-h-4k3$(mjsN#3Bkz0&+Gwre}?a~VE()!w^YigHP zXqVR2E-P!7)z|KHlerl1X2NAv!etf0W%Y&2))a2lj-J&OE-MR{)fevc!D1S)ksmK# zCE&xwH!1n6hL-uN0n9%402n7=XYweXzawcq3A1z47%TApiM+^UsvZS_eu{|o+NJ*c MAN>IYTeA@W0DaSLRsaA1 literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593948401893_NC b/temp/stateFiles/1593948401893_NC new file mode 100644 index 0000000000000000000000000000000000000000..8998b60c5c69ee9a5257c6960d677f91b99d5532 GIT binary patch literal 1250 zcmV<81ReVyiwFP!000000Hs*na@#}{-Zms{3=@X(b4&0ASJd8Lt(0ESHg1T)AteE3 zdIc+K{nIFtBCS$qn06RmgcsnBCt$eXnpyR<`A=D+|jLd*rdcbN1}n?|eF1 z>6d@NW}1Lzlnh#~|3W0Pm3pl#9I3RmAB8Fro;s0Ul=$0!{Qd0XAAkM#HfVIgmKVh% znRY=V5UF}HmZ$Qf2O8dNM7N{Bz20+iDcXSu2kldpjKaZ#9(b$p_)?t93-Aiu>VcgC zi!=8Nc{M}tj>606Om;i7^Nq8)>CU-4cV&_)@b+|$OpSu}k%;vKTiO;Sh36aUDwYa- z)YHprPnXwDy|ybw*e+K0wRU+hjsXB0`q)%pn>da|AS6tI%|jUkkpe$K+UJhVNZ%HH z;r17+ez%8i2L!AI~rB8i=nU64H4!3l^oLh`R zVN1&vmMbAbL?CVnOEStVEcH?H7y_ zwor)f(*m~)JPP*{DU{4{ligHz5RFkY7ca`foK9*w4)aS`gAC8GmxZx8@-;qRe>3kE z8fbogdOVR>vthV5$tsmy>Yg-;!hC*{I+;dU;!V`SmgH(J&x+K{ikewXM$*#AY5Q%? zabGBr#HrD_oLoX0wwKevYtrGY znk$guKn?TD$rMgF6%#Vm8z+&9X0suw^&yLK)=>O9oZsl|_DO#m0!%-h-4k3$(mjsN#3Bkz0&+Gwre}?a~VE()!w^YigHP zXqVR2E-P!7)z|KHlerl1X2NAv!etf0W%Y&2))a2lj-J&OE-MR{)fevc!D1S)ksmK# zCE&xwH!1n6hL-uN0n9%402n7=XYweXzawcq3A1z47%TApiM+^UsvZS_eu{|o+NJ*c MA1M#<(X$Z%0Q7-sng9R* literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593948455304_NC b/temp/stateFiles/1593948455304_NC new file mode 100644 index 0000000000000000000000000000000000000000..0cea88a26a2c0de2704a7bade194854c3c50ac8c GIT binary patch literal 1718 zcmV;n21)rJiwFP!000000HvB+bKEo($JLhJmQsdtd4#eLJkgCM>tdx3w8=JfA!$le znCTPvqD|a7_SV+6$uR9Od>-ET1Pl*6^U4e#ff+smQub0gwun5topiG293M&N{QmMS zzx)F(Rwd{c<^CW_UWTO@RPkV%r?MJs7P&0LSUwVQQ6^Xa`1{FcKmGdedC(hyOK~wt zMKuDwEUe_cBe5r*Z-ZVuKVmeL;KugT@I|9$(l+H84iRlF97Fkf4&?y(BFeKY|8Tu?vnNYFzbWQ0M_!xCJ35azj>`&OyK zCHUFHbR1(}cx>#Wm@}V?BnrlaBHtq+iSRuZLq-uK0UR?l_IMCQlnWLJj1!90a?be@+)1K%>`^vm z`a?tRC5eYHjuF9vAnx(d$CP`}(-RbwqA?<40dc|vhb#_N)8Es@8|dN{?|VjBzXH4q|EmT zXOKzoK16Kbsef=CGUTtzaw>ETf=Eq>5g$qLK`jQakHf4|K|Ki7?A4mQMhu$89LbM+ zKlX0k>=)5f5zDH-ep7!{25@t#5l$xA@gm~xR30p%keTUG5kISj8;b>+3GPcdS=hN@-#Om`^9n$->l-^4qBMaS#2&V?&De`Mh_QuV%Up(vlO8e+HO`ityI;gl`M~y2b(k6 z7jh>|^Q}_tHQHw$rP+QYs$yEk+DFXeav_vYwX)P6TF~=3#bR zrBywGwnhxoN~ZZq>zSD&Q}&^dVVXS-qfF0`(NpfbnJDFEad-lid2FgS@8O2Fnuo-h z2+O;fE-N>qDJhTyERGLi4g1KqG1%rp3*smDoK$YMSkZ=Y4rGOMkrB31XP zHZOkGywvGkiD0VFBRX;Wc-*lH5H?!*X1U8_N4ab?64mpdW*+3s#Hl%*3HY1V&v=lh-l?V*-l042up{x%f;@C(9(CZW zi}aC&FXYY_>c|(e@P%CY^3U9I|CbBwS!&^1c4xnBdv33V&$`vZrshUKy*oRt zHP6YmJ#2YBRnG~X>x*$qeTk*M7_~1JH#cuveKG1*Uu?N-!OeZ%Qs0)#h9;+Ndl{_f zA z0dE$W+gJ`PEC=o^2OU`sEG!4kEc0fOn-3-0Zg9L>9^TXhch|)Hd38PGYEs}fDR7Gv zxVsd1M=5ZN6u7GtcvA}8T?)(R{`H*hDrmdShn`Ia+#&<+E(6|C2HYY8?kWSdnns0< z6`on*0D)%q+6M-uYpu1=R!A8HkRpd`8|6lHA~vlKd)Yw z;FJ0vFX*@X1M}@Zn15~na8!cpCvUy=pCt_L<yR<`A=D+|jLdpu)(=j_?D-}&}v zrCa|@d!gjH`ueHmAaSQ<1(7!DOwu$3d1VX|T*gTX$5Gn8zq&q(poubtwWIxHF2AF|N8Cs55E6%1Kd8) zC5?oCpqYu1F4$JXM5ecefe2&!wl*=H708 zsR20ut1tQ^8ThGzXzZB?y&%f`Z}ghA1$}aD>Kq@dXEpluwf=)&|EgXkRZ=vQS{25v?{qzUmxzb00H=h~wa@@OYG|Vc~_% zNQX+r#p=w_M^~p`^=1YF4a31GkOgk8`Xuts($3?;WKwk$g@fqYWYThagbUcrYQJEd zu!TZ&pBA`f;8D1rNTFnoo9w2#gJ_JJxp+|)=5$iiahPvm4Kh5#UKYmY$k+IM{mr~v zXrTG|>G4Ej&4%ILB&$?*se95W3iJ6*>SP*ai8oOPTav4_JS$Q&D{5vn8A(ecr|q{n z$957OL3hRTnNQi5vNAya_|Q9To0R-jL(6>C0A`eY$dWZk$MulsH7;KhaFEqv0%-XDjtC<9)XtUl2)X~t(tL-Yv!Ep)7|HMf9p2C z{NtUkN^ek<`@=APAxb%{qTw`8RW;lya#e~*J(N*V#+U#2`|)Q#{rc}&Z*A;dh>A%n ztFgD1iAvo$l6&&mj<*)gdyGfQySDR0JQwSk$oJRxRGH@cH+Q@%ix&69p?v1O^3Lpd zR~NR)H2hi~&#jlz{CV+Ijz{zF=bkQI7Z2rOD9cKD@6N`Usx({Q6%#$c1??-!Ma#F; z@kA=`(;dCM_1W^)t7tuxB41yu?vZYC^JwCE-Z}k#M|p4GfIgwz2beSEUAZUnTu*+x z)a{k`vyTytgdj8)7zv+30!SEyCHy6W%M*&Ei`8{QSwfzIIh zi+}z0`$s?gdCoh#t!rC|cw48aD97Gqbx_LcpvdB*$rrc0`gP&OCH=pCpR2P5C__3v z13GK(1IUKH{sS9;LA0UDsXWOnfWYUBAgH_#8g1}KBC<+1S_1%bpE2gQ+Q3vw#OomvSJ#4tJkbO;Pcn6(f!@I6<Z z8Sl$-?zmyL|1v8=QCpE=9Otn-lJ%g}u*Nb;bNQewCfX4g4&64ID!D+ho)NOUH;+g6 z$8{SsgqTdS<3)?xQ+2Rt1SJ`|k1HH#>6&@(ss!n#qOT zOvrAL?-$E4keS83UAQotv)Wo@9N|VG#t#;DV%Up(tCT{?+HPhyt#sC>l`4<52b(k6 zmugp}`F5!{o7!jgs@Z-ht72M4wU3znaw)Y>m$Ixqv>9a*-WZ zY1M2&J6nv>N~QTp>6sZL)Ao^6BF!F&Fso-ad-li**Dd@_h_@Wnq8ty zit=_=rIVL?hCKir~E623WF?HwIxNdDm zU5#RDqnKJKrtTEeo)l9H#nhEz+NPMgQ`~+1-yC2yoS6-0YP)=N$C>uTnObnBt~j$c z&deR>j{7Lh=&NDPY*;f3*32Di))Q-H!J4^Z&DvNqcdR?^M>V6b#&lp~npv1;?o6|u zOfw79%$4b&&2-?-^qcqfnxR(%I+`cK%b%EnI;_$X6xVt9i_p6%`SCay_Nr79W zz}=<5drE;@q`+OJz}r&b?owFZ_ix5@S3%chKJaZa;1(HhcNy@WGT;^&a90`V)wD8f ztnkznFCU@25p^|&fp3Et_!f%wkj`1wLHecH`WQ$@a!zy`Wn-BRr{A+@QnR3I_50xKI9{?L59@6BJ=JnyLVuAV&gu78&>ypvB4P3J^;A3l`Nrm{YIWcqS-qB9`% O$NvE1r@LV%Apig`+H#u! literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593949721642_NC b/temp/stateFiles/1593949721642_NC new file mode 100644 index 0000000000000000000000000000000000000000..c42d595d1c76ba1d77554970eaf497e85b327620 GIT binary patch literal 1715 zcmV;k22A-MiwFP!000000HvB+bKEo($5jF?gaX6$5z0RB1RG1XZsmctSpo}514&_~ zPaxac#I0j*ZEc$jQ-MU{F>^!#I5+Dmko^;jGA1J=`t}Rf$9$$fT^&m;U(s{s%w(`tNgKa}2H~^=~W#a1qgqpd?#WyR6QyWsjV;;uN6kHC-M$}V_y zX`9aCFXh?7`eIf*E+5MAXmPypaOJvoB2Qvj)e5{e?_;L2d}~ikbq80qudJ4l&#SYk zRN(zxJ-w~@^tS3`E0&_zTF&mC4*B?W3IN#9-&Yjagph_I;vtC?xPDg@g&zA(rNb5Y zIf#+~hm$m=3Byzn5+sbuiA=*dkSs+AC2%4mI0*=u=l~pqB;gFFI8LZc0v@s?jtG~r zJ_>_~1z~^^L@;8L35yAnG-c*MXJ#plX+l!SCmf}a!~sHrDR3oJ;1K#Y}_=iHJaPL`jS#r6MJKA`qt(l0+am!4n=tdeL7u#T%O94L=w4u`b{X*Z%tL z_jkVka|1lLqnEZ6>5i^YS&hL<>bR2iaha#5(@$;z^SS!uMg7}+HxzjJBS>`BFsG0z z@Fqlb80a5(6H*l3RMkwH7z7avIK?4S;H^eXbaNu|TAo)Q@qkhqv|`Xa=2X4A`NQUo z8-p@_AQM#&Zr(75+7PV%)CZGH=4owNwC%nqlDwRy-{?7O5B%_%YqY<&up-O4E7K!c zEgT=&@t@^oEQ~d@jN>Aer_yvvENdz!Ss}lw%BglR%b{bFnUYHq&4@y``_9>De{5px z6k)1{qT_Pw*iuXh)YFt8juSuU4$yC8dI zaa6AQ2<<8!?xCgG9@X};;$hTSi1AlTJGSg)v0X`_q_Nx8&1zlsS*@xw?TPFW9Z9t( zvSO#wdyVngtqj`-vMy&;Vtm+cS4gRSdMPX8A4QG=B|ozH&oZ7}4)cPKzXfML+gOD>z4 z%1-3@reIdEdk(~N4(zhNa##1f==w06^>ZmVv)Hg!ila1P&x2wJp>Q~>$aoG~J_jyNsJ1!gAoObPHu$c$9 z^I-3FvaWBNS=Unnb}1opC?WD!Lex_UkwXcQuM%)u3D{o=jr)XVUVk-o*4U*6>`(*t zR|D>;2JBD+_EiIJQ;hv7ZrpD*^RCA+adC_t9AkfuaZir1gJbN=F==y5{5iJnTbo(e zqnNlTCJu^;KgFac#l%4|@uirwDJK3Dci;avJ6I2A>cW}0ZXf+|COvT`4xEWE&a{m) z^~bs6IZ8A8dRS8z*3^MD^~aj_#F{#=roLFyHrCW1>yGD9&Ft$j&0I`V2h-G_Y1)%% z>R_7sGR@jdGk>NpKG$oeUJq#I0-8C1X8u64opG~cH`R^Ywn zKQEZK`$PNfK3II-0N}I&ub#j4Hvg6|{Ir;z*v_c}Zy(4Iz8x=YP?C J!$c+_00093U-$q3 literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593949893442_NC b/temp/stateFiles/1593949893442_NC new file mode 100644 index 0000000000000000000000000000000000000000..47f268dcb90c7c933fbd4c8aa153b491e0f0c3b7 GIT binary patch literal 1718 zcmV;n21)rJiwFP!000000HvB+Z`?!_$6Z2iOI1X-ET1c(Qoc}3zQAn_5H$zD2+J!Gs+wN>_<<1;ho{Ql!x ze)-3_Sd~t{D93{^ei@W}P(_1jo{DO)Q{zFzHdBRxh@~`7r7i!8YUk1!o-a|m%%8)G~uon0*DY}QGyeWychw_TparmCWHe&L2eK*!Vw1q z#sPF`kdWA?#7j7*IPzJD+?aVY9fTq;3Sr;^ zPfjI41jC2}1_MuKqY?Hz3{dDNiO(X)BS096U<@$xeS(gUqSEAoH!*$~bf9t0DDh=-HSNSDLc@|fiMcSSOd6X5?_*=PV=>ea=aSadl7gnfQ z_jo+!<-&1GkN+|&!a!L)%{a`Npt?NEGE+7YYrJ3O$A?qsAlAu-P^O_ z!AQmEDT2u)n=K>mPQ}qO3hJ32716V5xV>DUp5VR^lck+$_Wjx6H+u_*=W34KG~-LV zUXcAF9~Y}JJiUsC`*3O2XSK7exQA+u7(HCtv1TvwostKFE4yCZw31byR-&9q57K8e z=3+lc^W9QzHOi+SrPzMNt72M4%7^via?Yhswz5ji(F=kpbgZc8t6f$a`d=L$oNBV))581S zd9r*V_wuyJRgZLN7wu8OxP1nmvd7 z$$2Xi=`lau6jTS>*FY@S;GZ{E#`Cc_QVpxYJcl3XdYqJnC>Eo!_F4HYv)Y|XQdOU7 z^J3@COP<}O2&U>hq7t`{$1JM=K%ChU0 z+G_>cuy1b;K;60nJj0Mpy%_pKnssdw-qs(lo~HxuooZ^}9oPd8IuZ{I$OBvEVF$jt zNFN&bg7$pDj(kA_U(l8>@7yi-f4M-ep%$KDclO%0=k{87v|BAaVi=Ozp4DpCyYtgp z^PFtj!&cW*^_;-Ez6iC{7aQt}VEbZGbMrRU7r}1zMTW~3)ZFI{^=-LqXmZ-Nmw|d7 z+{}Zm`=njp*0Zjs1Z+|QGAIGrD*<&>0x~E8*(w1ym4NM)P`gj4=e1WuYmH56zy>v7 zdo|#WYQP3HU|TidCdJsE;@bUIJ@0xP6BEbSz%jPx7GshcXVMX8V!)Z$;!K-3 zQ+u3So}<*WuZJ}?VNDHKQ+upwN35v3^HK$GGHBLzzi~Awla{bX;fHS z;khMVy+S#Ybv=fGYl0ZK28!j7)>W2Cx|P}Z8c1fcE_4%RZJ8FA-}84;^Q1lXdG)$< zKB@ohf_k|>&|mI5i=P{gb5c6jPv3g0ze*U~%csY>b0VA%AMxi?uFf8ryj`8h3UK-B Me};6b%_SiK0GC!>G5`Po literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593949998893_NC b/temp/stateFiles/1593949998893_NC new file mode 100644 index 0000000000000000000000000000000000000000..c0ad64cd771a621158b9ccd7a3c7d4d15b707dd9 GIT binary patch literal 1719 zcmV;o21xlIiwFP!000000HvDCa@<4|h80Y120{@oO9-=IMaJq@U!4VsJuw-ulVAs` zvVxY>Ggcx?6R9V$i%N>(dD!s;6bsgDsp1i+;t^yR$K-DoHo?#6%BpN&E6*5&5P% znMmb*x~rGBFP%kDD)!&WE_wD5eOiZf=Yl;40~A1x0vQD|gptq-x)3Pub_BHv$3g%u zM=0b7ijf4E5+C7FNEi*V9xY@eLZUeEso+vz5l9}60E;BnZGW#SU%x6}|DGrhbqOzC z{_D5jKlMyqHRZi^6A&5% zm{UNN_dbBM@9TeH6Hpjzs$wdu7ziNnImH1~-Uqc9v^f%Kse{%5fZV5)`i&Ski#b*w zum8Ay<3=wJpU6m+z0DidS8ec@zv?feM5b|RShVf2$f7i##^35SYY+JRwQKNTZ*GN# zbyvn?S+98}_}EgKu`{j=(n@J4q@R zcC#RRc{a|MV+3Xu5BA`~Y|d(XQSkuP8Zmshuw%oXXWNAoN>+BWx=E?4J}p&oqCLo* z(O9ZIkz_lC-fb$Mc~r&rzAW=;5mi2H9+yd}eY%xJ)o3KF=Yh>U(3y!db2=CB*R3HP zS_4pftw0<0?acwGTX#TU8M3JtOMghSu1&(*`V-Xibiln+O)b0wci=%s;(-Nu;L1Gg zz*iUPLknNfoiEstFKFQly7CpAyXF2b7s$8NBCzbvLEHA+UW0D)3-#6dyd-KnA&pR%>Yo~9$t3OQW-_52+rgNga5BKG>sjSW(slHvE=n6>v N>wh@AzZxhZ004k2PIUkP literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593950152800_NC b/temp/stateFiles/1593950152800_NC new file mode 100644 index 0000000000000000000000000000000000000000..58319429e01798d9bf4aa1c0068d27702002d724 GIT binary patch literal 1719 zcmV;o21xlIiwFP!000000HvB+Z`?!_$6Z2i0kxuB9--_5PjurM&ozCZO}3#6NmH7F z)F&9*vx!^B-rBQmR;U%?^YF$eKs@lwD-s_8iI2cId+9j#kg;~FRM~Tm&&-_j`;Tw= zXD4{BEI^^-%mdK>DPbX^45pmr6?aK zvK)HrsVLRG6L}z??|SRe{D|Q|c{g^SiWg!d71`m&fhv;h@b<3v?jquWIF`@7*WQI) z@7lsPo`he^>D+oH$zJ5oYCx_m5;Ls^u{yFMFZqLOrDPmJ{dm$WZ07LjkO z=~yc7(_Ov1joI=x%4j2$BHLK3?y(NJeKPhuZ%zMPSKd{GeEort=m8#xEYnlpDRlJ1 zNvOP^eIx`UVJsMw6cI)O?E3(c$Y(xgqtK5Cka0ZnM<9Su3Mv6YkuUftLXt&L5=tS1 zF&lBhuplwuJ{KZnP=AvQ`zVA=!cmNZ0LhRE4p0C&3S<<>5Jo~T=mJ*Wod{|Zj)eeR zj!?)E6e9^RB|gHVkT4ozJzB^{ghX-RQ^BRcB9J^B0TxND+x}ivzJ67{{(Vs%=@MSN z{MT>4fAqtjYu;Nsy1BWCcXW;NV(48}M};hp@-#jfe{sjFK9^oz(f?OJYs!1)79cbT zFsFbj?|lGi-`D@ZCZI6bRK-MAF%UrDbBY6~ybo$IXmcdeQU|RA0J%>o^&2s87IUIL zUjK3Z=FMInK9!Lwdz&|_uiD@*f7M?`iA>|tuxQ&8kws}fiNDor)*kTr8`ofeZ*GN# zbyvoRvY0z=nektyc_=DtU>Jv4EKg)LC^4+D93`22Sma~vV1`4-MiV6$ELJlL?C!1U zV1HP}m?^|~oK6=JcPHv-5e3amkMihQIoMh(&`fY&sqwHv+Y6(B`dpG-K5l2pOmVYY7a7J zbSTxHNV1(m?>3dsJgQ>*ku38`5mi2H9+yd}eY%xJ@3w=+T+>kd*Ate?tc$`==&J> z^}OV~m8s-do^A?N2iw;`9M{0k8!MIBp*pG>R)cv4KQQ$;E^?L6N8|0YVlA`UnP^h0 zKGo*M&zqM%!D|srs`E&dxP3hCSOowYt$efG<*~y|H5v))d0;aSbY|kroX!ROO>0Pp z)&SIAE6|30dvgHl)*TR7hHUD^(jU^SYm@M{{si?r9dPedQw#6F9eB`@cwj*uxH1nr z@YO~7(83pV=L>e^3tISsu6zaOZn^)<1@bMm2rRpE(6&9d*CL?ZY7r32kks|8R=eJv zoz|M?WZNFLyq>D(1kUwEsHMKxQeOny7mJ#kx2?VicB?P4T(+R*K5waS%Vk58)3&_~ z)brqG9_-vF2`eL_92yBb<+Y*Pca zr~$jH0e4gbwx|KSssT4C#_kl??zifBSL2x2IK~!^u{+1OBgfdnF?QvcG&v^j92@to z^{lH=Ol%Yr3&q5pV$zXfVxgG0QcRi@6L*T+@BixqtcEkS;Y@6|kM1~=jyMww&cqdG z+QgZ<uaYQdViV@*3^O)XecSFC9hYwC`5%k!vu_SKkXHm0eCY3j~2 z?Z`B>Fil;VW=*D(x`Q1~jt)%`8AOcc58EpqT||<_a`x0?j&HZPcER))Tw4 z%y_fN+{QAqu*}?9W*u2(7M7Va%e-0S=0l0L8yvruhc`9B-8C_PUR}?)niRNA3fv+E z?k)x1Q3~851@0;Z-jo7&m%{S7e?6zW3fgY-fp3!mx5$9I%Yb*30k_D2yUIYXrcq&I zh3A%d`3mJs*3}pWz71mFTPW5;I#*dI=~iazYapG;xzJ6Njb%Dqe$U=X&62KHKd)XF z-Y4}xU#Q;i_s#eF-u!dJ^G*uy+UZ;G>JJn8_p-^c=^QKX!$I~Bk!;A0&-0=hq7hH4843EGJkHE^doKk+3lo)ODCeFcHYKu#1py zGs{vF8@BBl>YP{}@wwx8!t+e-5srmWwSG6}tdn!rIbi8X@wR#Auit+E@cTbEz|DPC z(TI8bikK+rgDp8qL^_HBZxTQJ4CKGtFYl`V`LQ9vTldx6hCv+*SrWX5u+@R;5A7lg z8@n>eMDBxC#?Y~-fhBmq zJ<@@caj`mm^x@^n*MqsifSO@{9EbwfSA87u)3o=vFzHksM&U5JGU>FO9O44jv)U^d zH)yF4{ig+PYIqdxC4xzj<2t*kY9I37L6_ueF3-wnm=)2pnvNuuk+bgG zUf>>+EXTiv9S44gA%>75SdME};+mDXy(n`7QHe`CuoTK#MVuU;iRmq*V0$?oRzo_R zRdWR*9LiCCJL$p+XJSISdgCOL(R?-pm#5_{Nozj~xy*G?N(`-RQtBy~s)m{O-8}O0 z2~ktEB3vop)ruEm`$~dumJgTX7}YpcZPqF5jhsmH>R9Vkht`l)y%lQ9L)R-0A~jRe zruDI^yjDfNtUg9L&Kinehf`dmc+_0-cumQp8qMRTn#XJCYn{6v*XRp3*B4$>U${nJ zxT(I3m0kCLbwN-K7e-BkHkNhhhzB9)JVK)pr8YVAUu N{s)*(m9erB004C4Y)JqB literal 0 HcmV?d00001 diff --git a/temp/stateFiles/1593951867738_NC b/temp/stateFiles/1593951867738_NC new file mode 100644 index 0000000000000000000000000000000000000000..d146bc9337737d42a569f9f8aa7d8b86287c29bd GIT binary patch literal 1720 zcmV;p21ofHiwFP!000000HvB+Z`?!_$4x_T0Tod$k5Kl3C%W;B=bApyCfm@3gp{Np z^$EuIY~t3jxAttC6>5d}JiPG<5Dz@_io{1i;v+E5UOJ9FWUSpPRrZ|YGc)J>{^MJI z`Nz9l7G5tej{0HzLKL!JM*T^asItGEXQ~j9I*?Ib#Ml4$`|+ng{rc}EZ++-piSltG z%b~ZPic;M_m51`#uD2e|j~EV=cXRiNcrG?lksWOusv^mb?(BMRFCxAcC-Ry1%6nti zyRop1C*fCeI=5a+vgi3zIULNtuRUG5uAaz~P!^@~-kFUtQAxV7C&qe!E83SAi^wti_eh7_IUReRx2At?EAKi&zWzW+^Z;LrEYnlpDRlJ1 zNvOP^eIx`UVJsMw6cI)O?E3(c$Y(xgqtK5Cka0ZnM<9Su3Mv6YkuUftLXt&L5=tS1 zF&lBhuplwuJ{KZnP=AvQ`zVA=!cmNZ0LhRE4p0C&3S<<>5Jo~T=n@E&cQ=CChGQWB zmm?H%1jR@KOo@;1C?t%ASPvJn5g}0=_*8Hyum~g%M}S2V>%PBR)vsUGum3=l$GV1> zum1Jh?;rl~=bCqEM|U?D@s2K1UJSkK>bQ{Qah}Gf?o2r<|Dh2`wd`@uymG^!v25pW+TI!&603i1%rG6s@&SFm0 zN9#YX-@4Vy!zVIQWpDFV^;H}E<*)k7D3NJg8WwGPEV3xgC-Jv>&DsM#d+i$R@6D~y zu)}af!)0| z9qbRQ7&CRu0?h;ulo~JW+_3LW55L)+I|AQu>?EmN z*v*3M<=Iib93wERc(?}_W^-2Ci;4%R)`;Q5g&iCAJlig$P_nX{)lEuW^+~CUsrDdq zMn_WZi6q-8^mbGE%%du{4`i87im38o^SDe(?bEF+Di5h=+}2Y#(wofI)EJhShl@<4 z(=sXR5u`O@kd!LP&RS2+9I3Vsq!LN`NQ7xMLrTuM@1?R(+xf{EROYdX-oFQ1mDM~X zN~I|7rBzwE8C`05%l+_4Wv?$a8we#T-h41Ozg+lxNvs`9DtgsUs|~#`5BJYB*;_Zl zd%;Dre6IKNB+sfInb6MLQw8Jp83c|qK;{h2jzoj?{70uo}!W_<^a%agnQhJ{oVI6>FK*&P0=1 z^{F;5e$l-28D5KEQk_St#O>p8$0`8OXyxnWE{`2%s?kVT&jXuzpfeNa=5!(8uUkVp zv<9H|T7fp~+nWPWx9)(zGGtRPmi~}tU7Lir^(Uz3>41Bunp$`V?!beN!~+ZRz?FH} zfv+yohZeq|J72IPU(muAbmc3!aLfH)E|71jMPS*TgSPFty%quOR*QgGhNP}%wc7RW z?6lTAC)@V0<@HoOCvdJWLM`>hmii*tzF5@UylwSGuv>kR<+24e_jyZwTP_=#oVM*{ zpq>Xe^I+#b8P~VH-PO=qW1AYV zMGe?p4Y;Ekutg2nRSmdFF?Of8cE456yBf#D#xb^VjNLiL9XZApjGV91Pv#`vZS?0|mHy=v0-Qf7OJiMt1?yiaX^XhuW)ug~}Qs5RT zaCa&2j#A(jDR5UQ@TL^FyA+ns{p&g1RnT^u4}6;pxJ3rsT?V|P47f!G+*Jm8HH``z zE4;A8%U3ApvaZH3@NEzS-$Jn-(z(hyNw+dvUjyk(&V_EGY%J5^@_Y79YL;}p`g!@P z@IJ2p{X+GEzi+