agent-backend/src/main/java/org/bdware/server/CMHttpServer.java
2023-02-08 13:59:03 +08:00

379 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package org.bdware.server;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.ChannelHandler.Sharable;
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.OpenSsl;
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 io.prometheus.client.exporter.HTTPServer;
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.*;
import org.bdware.sc.bean.Contract;
import org.bdware.sc.bean.ContractExecType;
import org.bdware.sc.db.CMTables;
import org.bdware.sc.db.KeyValueDBUtil;
import org.bdware.sc.db.MultiIndexTimeRocksDBUtil;
import org.bdware.sc.util.ExceptionUtil;
import org.bdware.sdk.consistency.ConsistencyPluginManager;
import org.bdware.server.action.FileActions;
import org.bdware.server.action.UserManagerAction;
import org.bdware.server.doip.ContractRepositoryMain;
import org.bdware.server.http.CMHttpHandler;
import org.bdware.server.nodecenter.client.NodeCenterClientHandler;
import org.bdware.server.tcp.TCPClientFrameHandler;
import org.bdware.server.ws.ContractManagerFrameHandler;
import org.bdware.units.NetworkManager;
import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class CMHttpServer {
private static final Logger LOGGER = LogManager.getLogger(CMHttpServer.class);
private static final String CONFIG_PATH = "cmconfig.json";
public static EventLoopGroup workerGroup = new NioEventLoopGroup();
public static MultiIndexTimeRocksDBUtil nodeLogDB =
new MultiIndexTimeRocksDBUtil(
"./ContractManagerDB", CMTables.LocalNodeLogDB.toString());
public static URLClassLoader pluginLoader;
private static SslContext sslContext = null;
final String PATH = "/SCIDE/SCExecutor";
private final int port;
private CMHttpServer(int port) {
this.port = port;
}
private static void configServer(CMDConf cmdConf) throws IOException {
if (cmdConf.disableDoRepo) {
DoConfig.callContractUsingDOI = false;
}
GlobalConf.instance.isLAN = cmdConf.isLAN;
if (!cmdConf.doipCertPath.isEmpty()) {
String[] conf = cmdConf.doipCertPath.split(":");
// DOAConf.certPath = conf[0];
// DOAConf.certPassword = conf[1];
}
if (!cmdConf.doipUserHandle.isEmpty()) {
DOAConf.repoDoid = cmdConf.doipUserHandle;
}
if (!cmdConf.doipLhsAddress.isEmpty()) {
DOAConf.lhsAddress = cmdConf.doipLhsAddress;
}
if (cmdConf.withBdledgerServer) {
ContractManager.threadPool.execute(
() -> NetworkManager.instance.initP2P(cmdConf.servicePort + 4));
}
// 可自动运行bdledger可执行文件也可在shell脚步中运行和停止
if (!cmdConf.withBdledgerClient.isEmpty()) {
ContractManager.scheduledThreadPool.schedule(
() -> {
File ledgerClient = new File(cmdConf.withBdledgerClient);
LOGGER.debug("canRead=" + ledgerClient.canRead() +
" path=" + ledgerClient.getAbsolutePath());
try {
Runtime.getRuntime().exec(ledgerClient.getAbsolutePath());
} catch (IOException e) {
LOGGER.warn("start bdledger client failed: " + e.getMessage());
}
},
1, TimeUnit.SECONDS);
}
if (cmdConf.enableEventPersistence) {
ContractManager.eventPersistenceEnabled = true;
}
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)
.ciphers(
null,
(ciphers, defaultCiphers, supportedCiphers) ->
defaultCiphers.stream()
.filter(x -> null != x && !x.contains("RC4"))
.toArray(String[]::new))
.build();
LOGGER.info("openssl isAvailable:" + OpenSsl.isAvailable());
}
} catch (Exception e) {
LOGGER.warn("Enabling SSL failed: " + e.getMessage());
LOGGER.debug(ExceptionUtil.exceptionToString(e));
}
}
// cmi file
ContractClient.cmi = cmdConf.cmi;
FileWriter fw = new FileWriter("CMI");
fw.write(ContractClient.cmi);
fw.close();
LOGGER.info("write CMI=" + ContractClient.cmi + " to ./CMI");
FileActions.setTextFileSuffixes(cmdConf.textFileSuffixes);
// plugins
CMHttpHandler.wsPluginActions = parseStrAsList(cmdConf.wsPluginActions);
TCPClientFrameHandler.clientToAgentPlugins = parseStrAsList(cmdConf.clientToAgentPlugins);
NodeCenterClientHandler.clientToClusterPlugins = parseStrAsList(cmdConf.clientToClusterPlugins);
org.bdware.units.tcp.TCPClientFrameHandler.tcpPlugins = parseStrAsList(cmdConf.tcpPlugins);
if (!cmdConf.debug.isEmpty()) {
try {
String[] classes = cmdConf.debug.split(",");
for (String clz : classes) {
if ("all".equalsIgnoreCase(clz)) {
Configurator.setRootLevel(Level.DEBUG);
break;
}
Configurator.setLevel(clz, Level.DEBUG);
LOGGER.warn("set debug: " + clz);
}
} catch (Exception e) {
LOGGER.warn(e.getMessage());
}
}
if (cmdConf.startContract != null && cmdConf.startContract.size() > 0) {
ContractManager.scheduledThreadPool.schedule(
() -> {
try {
for (JsonElement je : cmdConf.startContract) {
JsonObject jo = je.getAsJsonObject();
if (!jo.has("path"))
continue;
String path = jo.get("path").getAsString();
File f = new File(path);
if (!f.getName().endsWith(".ypk") || !f.exists())
continue;
Contract c = new Contract();
c.setScript(f.getAbsolutePath());
c.setType(ContractExecType.Sole);
if (jo.has("killBeforeStart")) {
ContractManager.instance.stopContract(jo.get("killBeforeStart").getAsString());
}
if (jo.has("owner"))
c.setOwner(jo.get("owner").getAsString());
else
c.setOwner(UserManagerAction.getNodeManager());
if (jo.has("createParam"))
c.setCreateParam(jo.get("createParam"));
ContractManager.instance.startContract(c);
}
} catch (Exception e) {
e.printStackTrace();
}
},
10, TimeUnit.SECONDS);
}
if (cmdConf.datachainConf != null) {
GlobalConf.resetDataChain(cmdConf.datachainConf);
}
if (cmdConf.overwrite) {
cmdConf.write(CONFIG_PATH);
}
}
private static String[] parseStrAsList(String str) {
if (str == null) {
return new String[]{};
}
return str.split(",");
}
private static void addDirToPath(String s) {
try {
LOGGER.info("add to path: " + s);
// Field field = ClassLoader.class.getDeclaredField("sys_paths");
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] path = (String[]) field.get(null);
String[] temp = new String[path.length + 1];
System.arraycopy(path, 0, temp, 0, path.length);
temp[path.length] = s;
field.set(null, temp);
} catch (Exception e) {
LOGGER.error(e.getMessage());
LOGGER.debug(ExceptionUtil.exceptionToString(e));
}
}
public static void main(String[] args) throws IOException {
RecoverMechTimeRecorder.startCMHttpServer = System.currentTimeMillis();
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 cmdConf = CMDConf.parseFile(CONFIG_PATH);
// addDirToPath(new File("./dynamicLibrary").getAbsolutePath());
// for compatibility
if (cmdConf.cmi.isEmpty()) {
cmdConf.overwrite = true;
File cmiF = new File("./CMI");
try {
BufferedReader br = new BufferedReader(new FileReader(cmiF));
cmdConf.cmi = br.readLine().replaceAll("\\s+", "");
br.close();
LOGGER.info("read CMI from ./CMI file");
} catch (Exception ignored) {
}
}
cmdConf.parseArgs(args);
configServer(cmdConf);
// check default key pair
File keyFile = new File("manager.key");
if (keyFile.exists()) {
try {
BufferedReader br = new BufferedReader(new FileReader(keyFile));
String pubKey = br.readLine();
String nowManager =
KeyValueDBUtil.instance.getValue(
CMTables.ConfigDB.toString(), "__NodeManager__");
// manager.key is used when node manager isn' set
if (null == nowManager || nowManager.isEmpty()) {
KeyValueDBUtil.instance.setValue(
CMTables.ConfigDB.toString(), "__NodeManager__", pubKey);
KeyValueDBUtil.instance.setValue(
CMTables.NodeRole.toString(), pubKey,
"NodeManager,ContractProvider,ContractUser,ContractInstanceManager");
KeyValueDBUtil.instance.setValue(
CMTables.NodeTime.toString(),
pubKey,
Long.toString(new Date().getTime()));
LOGGER.info("set node manager from manager.key");
}
} catch (IOException ignored) {
}
}
start(cmdConf.ip, cmdConf.servicePort, cmdConf);
}
public static void start(String ip, int port, CMDConf cmdConf) throws IOException {
LOGGER.info("start server at:" + port);
GlobalConf.initMasterAddress(ip + ":" + (port + 1));
GlobalConf.initIpPort(ip + ":" + port);
LOGGER.debug("dir:" + new File("./").getAbsolutePath());
new CMHttpServer(port).start();
ContractRepositoryMain.start();
}
/**
* port: http & websocket port port+1: tcp port port+2: doip port port+3: prometheus
*/
private void start() {
ConsistencyPluginManager.setContext(new SDKContext());
// EpollEventLoopGroup
// EpollServerSocketChannel
// ContractManager.reconnectPort = (port - 18000) * 30 + 1630;
// if (ContractManager.reconnectPort < 0) ContractManager.reconnectPort = 1630;
File[] pluginJar = new File("./pluginLib/")
.listFiles(pathname -> pathname.getName().endsWith(".jar"));
URL[] urls;
if (pluginJar != null && pluginJar.length > 0) {
urls = new URL[pluginJar.length];
for (int i = 0; i < pluginJar.length; i++) {
try {
urls[i] = pluginJar[i].toURI().toURL();
LOGGER.info("add plugin:" + pluginJar[i].getName());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
} else {
urls = new URL[]{};
}
pluginLoader = new URLClassLoader(urls, CMHttpServer.class.getClassLoader());
if (port >= 18000 && port < 18100) {
ContractManager.cPort = new ContractPort(1615 + (port - 18000) * 30);
} else {
ContractManager.cPort = new ContractPort(1615);
}
final CMHttpHandler serverHandler = new CMHttpHandler();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
NettyConnectServerHandler trafficSharp =
new NettyConnectServerHandler(new AtomicInteger(0));
try {
ServerBootstrap b1 = new ServerBootstrap();
b1.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
b1.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(port)
.childHandler(
new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel arg0) {
if (sslContext != null) {
arg0.pipeline().addLast(new OptionalSslHandler(sslContext));
}
arg0.pipeline()
.addLast(trafficSharp)
.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(10 * 1024 * 1024))
.addLast(
new WebSocketServerProtocolHandler(
PATH, null, true))
.addLast(new ChunkedWriteHandler())
.addLast(serverHandler)
.addLast(new ContractManagerFrameHandler());
}
});
final Channel ch = b1.bind(port).sync().channel();
LOGGER.debug("[CMHttpServer] listen master port at:" + port);
new HTTPServer(port + 3);
NetworkManager.instance.initTCP(port + 1, workerGroup);
ch.closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
@Sharable
public static class NettyConnectServerHandler extends ChannelInboundHandlerAdapter {
public NettyConnectServerHandler(AtomicInteger connectNum) {
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
super.channelRegistered(ctx);
CongestionControl.httpCControl(ctx);
}
}
}