Files
redkale/src/main/java/org/redkale/boot/Application.java
2023-03-27 19:45:39 +08:00

2621 lines
128 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.

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkale.boot;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.net.http.HttpClient;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.logging.*;
import org.redkale.annotation.Resource;
import org.redkale.boot.ClassFilter.FilterEntry;
import org.redkale.cluster.*;
import org.redkale.convert.Convert;
import org.redkale.convert.bson.BsonFactory;
import org.redkale.convert.json.*;
import org.redkale.mq.*;
import org.redkale.net.*;
import org.redkale.net.http.*;
import org.redkale.net.sncp.*;
import org.redkale.service.Service;
import org.redkale.source.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.*;
import org.redkale.watch.WatchServlet;
/**
*
* 进程启动类,全局对象。 <br>
* <pre>
* 程序启动执行步骤:
* 1、读取application.xml
* 2、进行classpath扫描动态加载Service、WebSocket与Servlet
* 3、优先加载所有SNCP协议的服务再加载其他协议服务 最后加载WATCH协议的服务
* 4、最后进行Service、Servlet与其他资源之间的依赖注入
* </pre>
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*/
public final class Application {
/**
* 当前进程启动的时间, 类型: long
*/
public static final String RESNAME_APP_TIME = "APP_TIME";
/**
* 当前进程服务的名称, 类型String
*/
public static final String RESNAME_APP_NAME = "APP_NAME";
/**
* 当前进程的根目录, 类型String、File、Path、URI
*/
public static final String RESNAME_APP_HOME = "APP_HOME";
/**
* 当前进程的配置目录URI如果不是绝对路径则视为HOME目录下的相对路径 类型String、URI、File、Path <br>
* 若配置目录不是本地文件夹, 则File、Path类型的值为null
*/
public static final String RESNAME_APP_CONF_DIR = "APP_CONF_DIR";
/**
* 当前进程的配置文件, 类型String、URI、File、Path <br>
* 一般命名为: application.xml、application.properties 若配置文件不是本地文件, 则File、Path类型的值为null
*/
public static final String RESNAME_APP_CONF_FILE = "APP_CONF_FILE";
/**
* 当前进程节点的nodeid 类型int
*/
public static final String RESNAME_APP_NODEID = "APP_NODEID";
/**
* 当前进程节点的IP地址 类型InetSocketAddress、InetAddress、String
*/
public static final String RESNAME_APP_ADDR = "APP_ADDR";
/**
* 当前进程的work线程池 类型Executor、ExecutorService
*
* @since 2.3.0
*/
public static final String RESNAME_APP_EXECUTOR = "APP_EXECUTOR";
/**
* 当前进程的客共享AsyncGroup, 有且只有一个server节点NodeServer此字段值才有效
*
* @since 2.8.0
*/
public static final String RESNAME_APP_GLOBAL_IOGROUP = "APP_GLOBAL_IOGROUP";
/**
* 当前进程的客户端组, 类型AsyncGroup
*
* @since 2.8.0
*/
public static final String RESNAME_APP_CLIENT_IOGROUP = "APP_CLIENT_IOGROUP";
/**
* 使用RESNAME_APP_CLIENT_IOGROUP代替
*
* @since 2.3.0
*
*/
@Deprecated(since = "2.8.0")
public static final String RESNAME_APP_CLIENT_ASYNCGROUP = "APP_CLIENT_ASYNCGROUP";
/**
* 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String <br>
*/
public static final String RESNAME_SNCP_ADDRESS = "SNCP_ADDRESS";
/**
* 当前Service所属的SNCP Server所属的组 类型: String<br>
*/
public static final String RESNAME_SNCP_GROUP = "SNCP_GROUP";
/**
* "SERVER_ROOT" 当前Server的ROOT目录类型String、File、Path
*/
public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT;
/**
* 当前Server的ResourceFactory
*/
public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY";
//UDP协议的ByteBuffer Capacity
private static final int UDP_CAPACITY = 1024;
//本进程节点ID
final int nodeid;
//本进程节点ID
final String name;
//本地IP地址
final InetSocketAddress localAddress;
//业务逻辑线程池
//@since 2.3.0
final ExecutorService workExecutor;
//日志配置资源
//@since 2.8.0
final Properties loggingProperties = new Properties();
//Source 原始的配置资源, 只会存在redkale.datasource(.|[) redkale.cachesource(.|[)开头的配置项
final Properties sourceProperties = new Properties();
//CacheSource 资源
final List<CacheSource> cacheSources = new CopyOnWriteArrayList<>();
private final ReentrantLock cacheSourceLock = new ReentrantLock();
//DataSource 资源
final List<DataSource> dataSources = new CopyOnWriteArrayList<>();
private final ReentrantLock dataSourceLock = new ReentrantLock();
//NodeServer 资源, 顺序必须是sncps, others, watchs
final List<NodeServer> servers = new CopyOnWriteArrayList<>();
//配置项里的group信息, 注意: 只给SNCP使用
private final SncpRpcGroups sncpRpcGroups = new SncpRpcGroups();
//
private final AsyncIOGroup globalAsyncGroup;
//给客户端使用包含SNCP客户端、自定义数据库客户端连接池
private final AsyncIOGroup clientAsyncGroup;
//配置源管理接口
//@since 2.7.0
private PropertiesAgent propertiesAgent;
//只存放不以system.property.、mimetype.property.、redkale.开头的配置项
private final Properties envProperties = new Properties();
//envProperties更新锁
private final ReentrantLock envPropertiesLock = new ReentrantLock();
//配置信息只读版Properties
private final Environment environment;
//第三方服务配置资源
//@since 2.8.0
final Properties clusterProperties = new Properties();
//第三方服务发现管理接口
//@since 2.1.0
private final ClusterAgent clusterAgent;
//MQ管理配置资源
//@since 2.8.0
final Properties messageProperties = new Properties();
//MQ管理接口
//@since 2.1.0
private final MessageAgent[] messageAgents;
//是否从/META-INF中读取配置
private final boolean configFromCache;
//全局根ResourceFactory
final ResourceFactory resourceFactory = ResourceFactory.create();
//服务配置项
final AnyValue config;
//排除的jar路径
final String excludelibs;
//临时计数器
CountDownLatch servicecdl; //会出现两次赋值
//是否启动了WATCH协议服务
boolean watching;
//--------------------------------------------------------------------------------------------
//是否用于main方法运行
private final boolean singletonMode;
//是否用于编译模式运行
private final boolean compileMode;
//进程根目录
private final File home;
//进程根目录
private final String homePath;
//配置文件目录
private final URI confPath;
//日志
private final Logger logger;
//监听事件
final List<ApplicationListener> listeners = new CopyOnWriteArrayList<>();
//服务启动时间
private final long startTime = System.currentTimeMillis();
//Server启动的计数器用于确保所有Server都启动完后再进行下一步处理
private final CountDownLatch shutdownLatch;
//根ClassLoader
private final RedkaleClassLoader classLoader;
//Server根ClassLoader
private final RedkaleClassLoader serverClassLoader;
@SuppressWarnings("UseSpecificCatch") //config: 不带redkale的配置项
Application(final boolean singletonMode, boolean compileMode, final AnyValue config) {
this.singletonMode = singletonMode;
this.compileMode = compileMode;
this.config = config;
this.configFromCache = "true".equals(config.getValue("[config-from-cache]"));
this.environment = new Environment(this.envProperties);
//设置APP_HOME、APP_NAME
this.name = checkName(config.getValue("name", ""));
this.resourceFactory.register(RESNAME_APP_NAME, name);
System.setProperty(RESNAME_APP_NAME, name);
final File root = new File(System.getProperty(RESNAME_APP_HOME));
this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime);
this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath());
this.resourceFactory.register(RESNAME_APP_HOME, File.class, root);
this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI());
File confFile = null;
try { //设置APP_HOME
this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath());
if (System.getProperty(RESNAME_APP_HOME) == null) {
System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath());
}
this.home = root.getCanonicalFile();
this.homePath = this.home.getPath();
String confDir = System.getProperty(RESNAME_APP_CONF_DIR, "conf");
if (confDir.contains("://") || confDir.startsWith("file:") || confDir.startsWith("resource:") || confDir.contains("!")) { //graalvm native-image startwith resource:META-INF
this.confPath = URI.create(confDir);
if (confDir.startsWith("file:")) {
confFile = new File(this.confPath.getPath()).getCanonicalFile();
}
} else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) {
confFile = new File(confDir).getCanonicalFile();
this.confPath = confFile.toURI();
} else {
confFile = new File(this.home, confDir).getCanonicalFile();
this.confPath = confFile.toURI();
}
} catch (IOException e) {
throw new RedkaleException(e);
}
{ //设置系统变量
AnyValue propertiesConf = this.config.getAnyValue("properties");
if (propertiesConf != null) { //设置配置文件中的系统变量
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name", "");
if (key.startsWith("system.property.")) {
String propName = key.substring("system.property.".length());
if (System.getProperty(propName) == null) { //命令行传参数优先级高
String value = prop.getValue("value");
//replaceValue调用前必须给home、name赋值
System.setProperty(propName, value == null ? value : replaceValue(value));
}
}
}
}
//设置默认系统变量
if (System.getProperty("redkale.convert.pool.size") == null) {
System.setProperty("redkale.convert.pool.size", "128");
}
if (System.getProperty("redkale.convert.writer.buffer.defsize") == null) {
System.setProperty("redkale.convert.writer.buffer.defsize", "4096");
}
if (System.getProperty("redkale.trace.enable") == null) {
System.setProperty("redkale.trace.enable", "false");
}
System.setProperty("redkale.version", Redkale.getDotedVersion());
int nid = config.getIntValue("nodeid", 0);
this.nodeid = nid;
this.resourceFactory.register(RESNAME_APP_NODEID, nid);
System.setProperty(RESNAME_APP_NODEID, "" + nid);
}
String localaddr = config.getValue("address", "").trim();
InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress();
this.localAddress = new InetSocketAddress(addr, config.getIntValue("port"));
this.resourceFactory.register(RESNAME_APP_ADDR, addr.getHostAddress());
this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr);
this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress);
this.resourceFactory.register(RESNAME_APP_CONF_DIR, URI.class, this.confPath);
this.resourceFactory.register(RESNAME_APP_CONF_DIR, String.class, this.confPath.toString());
if (confFile != null) {
this.resourceFactory.register(RESNAME_APP_CONF_DIR, File.class, confFile);
this.resourceFactory.register(RESNAME_APP_CONF_DIR, Path.class, confFile.toPath());
}
this.resourceFactory.register(Environment.class, environment);
{ //初始化ClassLoader
ClassLoader currClassLoader = Thread.currentThread().getContextClassLoader();
if (currClassLoader instanceof RedkaleClassLoader) {
this.classLoader = (RedkaleClassLoader) currClassLoader;
} else {
Set<String> cacheClasses = null;
if (!singletonMode && !compileMode) {
try {
InputStream in = Application.class.getResourceAsStream(RedkaleClassLoader.RESOURCE_CACHE_CLASSES_PATH);
if (in != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8), 1024);
List<String> list = new ArrayList<>();
reader.lines().forEach(v -> list.add(v));
Collections.sort(list);
if (!list.isEmpty()) {
cacheClasses = new LinkedHashSet<>(list);
}
in.close();
}
} catch (Exception e) {
}
}
if (cacheClasses == null) {
this.classLoader = new RedkaleClassLoader(currClassLoader);
} else {
this.classLoader = new RedkaleClassLoader.RedkaleCacheClassLoader(currClassLoader, cacheClasses);
}
Thread.currentThread().setContextClassLoader(this.classLoader);
}
}
{ //以下是初始化日志配置
URI logConfURI;
File logConfFile = null;
if (configFromCache) {
logConfURI = RedkaleClassLoader.getConfResourceAsURI(null, "logging.properties");
} else if ("file".equals(confPath.getScheme())) {
logConfFile = new File(confPath.getPath(), "logging.properties");
logConfURI = logConfFile.toURI();
if (!logConfFile.isFile() || !logConfFile.canRead()) {
logConfFile = null;
}
} else {
logConfURI = URI.create(confPath + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties");
}
if (!"file".equals(confPath.getScheme()) || logConfFile != null) {
try {
InputStream fin = logConfURI.toURL().openStream();
Properties properties0 = new Properties();
properties0.load(fin);
fin.close();
reconfigLogging(properties0);
} catch (IOException e) {
Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e);
}
}
if (compileMode) {
RedkaleClassLoader.putReflectionClass(java.lang.Class.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(SimpleFormatter.class, SimpleFormatter.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(LoggingSearchHandler.class, LoggingSearchHandler.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(LoggingFileHandler.class, LoggingFileHandler.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(LoggingFileHandler.LoggingFormater.class, LoggingFileHandler.LoggingFormater.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(LoggingFileHandler.LoggingConsoleHandler.class, LoggingFileHandler.LoggingConsoleHandler.class.getName());
RedkaleClassLoader.putReflectionPublicConstructors(LoggingFileHandler.LoggingSncpFileHandler.class, LoggingFileHandler.LoggingSncpFileHandler.class.getName());
}
}
this.logger = Logger.getLogger(this.getClass().getSimpleName());
this.shutdownLatch = new CountDownLatch(config.getAnyValues("server").length + 1);
logger.log(Level.INFO, colorMessage(logger, 36, 1, "-------------------------------- Redkale " + Redkale.getDotedVersion() + " --------------------------------"));
//------------------------------------ 基本设置 ------------------------------------
{
final String confDir = this.confPath.toString();
logger.log(Level.INFO, "APP_OS = " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch") + "\r\n"
+ "APP_JAVA = " + System.getProperty("java.runtime.name", System.getProperty("org.graalvm.nativeimage.kind") != null ? "Nativeimage" : "")
+ " " + System.getProperty("java.runtime.version", System.getProperty("java.vendor.version", System.getProperty("java.vm.version"))) + "\r\n" //graalvm.nativeimage 模式下无 java.runtime.xxx 属性
+ "APP_PID = " + ProcessHandle.current().pid() + "\r\n"
+ RESNAME_APP_NAME + " = " + this.name + "\r\n"
+ RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n"
+ "APP_LOADER = " + this.classLoader.getClass().getSimpleName() + "\r\n"
+ RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n"
+ RESNAME_APP_HOME + " = " + homePath + "\r\n"
+ RESNAME_APP_CONF_DIR + " = " + confDir.substring(confDir.indexOf('!') + 1));
if (!compileMode && !(classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader)) {
String lib = replaceValue(config.getValue("lib", "${APP_HOME}/libs/*").trim());
lib = lib.isEmpty() ? confDir : (lib + ";" + confDir);
Server.loadLib(classLoader, logger, lib);
}
this.resourceFactory.register(BsonFactory.root());
this.resourceFactory.register(JsonFactory.root());
this.resourceFactory.register(BsonFactory.root().getConvert());
this.resourceFactory.register(JsonFactory.root().getConvert());
this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert());
this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert());
}
//------------------------------------ 读取配置 ------------------------------------
try {
loadResourceProperties();
} catch (IOException e) {
throw new RedkaleException(e);
}
//------------------------------------ 配置 <transport> 节点 ------------------------------------
String excludelib0 = null;
ClusterAgent cluster = null;
MessageAgent[] mqs = null;
int bufferCapacity = 32 * 1024;
int bufferPoolSize = Utility.cpus() * 8;
AnyValue executorConf = null;
executorConf = config.getAnyValue("executor");
AnyValue excludelibConf = config.getAnyValue("excludelibs");
if (excludelibConf != null) {
excludelib0 = excludelibConf.getValue("value");
}
AnyValue clusterConf = config.getAnyValue("cluster");
if (clusterConf != null) {
try {
String classVal = clusterConf.getValue("type", clusterConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: consul, nacos
Iterator<ClusterAgentProvider> it = ServiceLoader.load(ClusterAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(ClusterAgentProvider.class);
while (it.hasNext()) {
ClusterAgentProvider provider = it.next();
if (provider != null) {
RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
}
if (provider != null && provider.acceptsConf(clusterConf)) {
cluster = provider.createInstance();
cluster.setConfig(clusterConf);
break;
}
}
if (cluster == null) {
ClusterAgent cacheClusterAgent = new CacheClusterAgent();
if (cacheClusterAgent.acceptsConf(clusterConf)) {
cluster = cacheClusterAgent;
cluster.setConfig(clusterConf);
}
}
if (cluster == null) {
logger.log(Level.SEVERE, "load application cluster resource, but not found name='type' value error: " + clusterConf);
}
} else {
Class type = classLoader.loadClass(classVal);
if (!ClusterAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance();
cluster.setConfig(clusterConf);
}
}
//此时不能执行cluster.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e);
}
}
AnyValue[] mqConfs = config.getAnyValues("mq");
if (mqConfs != null && mqConfs.length > 0) {
mqs = new MessageAgent[mqConfs.length];
Set<String> mqnames = new HashSet<>();
for (int i = 0; i < mqConfs.length; i++) {
AnyValue mqConf = mqConfs[i];
String mqname = mqConf.getValue("name", "");
if (mqnames.contains(mqname)) {
throw new RedkaleException("mq.name(" + mqname + ") is repeat");
}
mqnames.add(mqname);
String namex = mqConf.getValue("names");
if (namex != null && !namex.isEmpty()) {
for (String n : namex.split(";")) {
if (n.trim().isEmpty()) {
continue;
}
if (mqnames.contains(n.trim())) {
throw new RedkaleException("mq.name(" + n.trim() + ") is repeat");
}
mqnames.add(n.trim());
}
}
try {
String classVal = mqConf.getValue("type", mqConf.getValue("value")); //兼容value字段
if (classVal == null || classVal.isEmpty() || classVal.indexOf('.') < 0) { //不包含.表示非类名,比如值: kafka, pulsar
Iterator<MessageAgentProvider> it = ServiceLoader.load(MessageAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(MessageAgentProvider.class);
while (it.hasNext()) {
MessageAgentProvider provider = it.next();
if (provider != null) {
RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); //loader class
}
if (provider != null && provider.acceptsConf(mqConf)) {
mqs[i] = provider.createInstance();
mqs[i].setConfig(mqConf);
break;
}
}
if (mqs[i] == null) {
logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf);
}
} else {
Class type = classLoader.loadClass(classVal);
if (!MessageAgent.class.isAssignableFrom(type)) {
logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf);
} else {
RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName());
mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance();
mqs[i].setConfig(mqConf);
}
}
//此时不能执行mq.init因内置的对象可能依赖config.properties配置项
} catch (Exception e) {
logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e);
}
}
}
ExecutorService workExecutor0 = null;
if (executorConf == null) {
executorConf = DefaultAnyValue.create();
}
final int workThreads = executorConf.getIntValue("threads", Utility.cpus() * 4);
boolean workHash = executorConf.getBoolValue("hash", false);
if (workThreads > 0) {
if (workHash) {
workExecutor0 = WorkThread.createHashExecutor(workThreads, "Redkale-HashWorkThread-%s");
} else {
workExecutor0 = WorkThread.createExecutor(workThreads, "Redkale-WorkThread-%s");
}
}
ExecutorService clientExecutor = workExecutor0;
if (clientExecutor == null) {
//给所有client给一个默认的ExecutorService
final int clientThreads = executorConf.getIntValue("clients", Utility.cpus());
clientExecutor = WorkThread.createExecutor(clientThreads, "Redkale-DefaultClient-WorkThread-%s");
}
this.workExecutor = workExecutor0;
this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor);
this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor);
if (config.getAnyValues("server").length < 2) { //只存在一个server节点
this.globalAsyncGroup = new AsyncIOGroup("Redkale-Global-IOThread-%s", workExecutor, bufferCapacity, bufferPoolSize).skipClose(true);
this.resourceFactory.register(RESNAME_APP_GLOBAL_IOGROUP, AsyncGroup.class, this.globalAsyncGroup);
this.resourceFactory.register(RESNAME_APP_GLOBAL_IOGROUP, AsyncIOGroup.class, this.globalAsyncGroup);
this.clientAsyncGroup = this.globalAsyncGroup;
} else {
this.globalAsyncGroup = null;
this.clientAsyncGroup = new AsyncIOGroup("Redkale-DefaultClient-IOThread-%s", clientExecutor, bufferCapacity, bufferPoolSize).skipClose(true);
}
this.resourceFactory.register(RESNAME_APP_CLIENT_IOGROUP, AsyncGroup.class, this.clientAsyncGroup);
this.resourceFactory.register(RESNAME_APP_CLIENT_IOGROUP, AsyncIOGroup.class, this.clientAsyncGroup);
this.resourceFactory.register(RESNAME_APP_CLIENT_ASYNCGROUP, AsyncGroup.class, this.clientAsyncGroup);
this.excludelibs = excludelib0;
this.clusterAgent = cluster;
this.messageAgents = mqs;
if (compileMode || this.classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader) {
this.serverClassLoader = this.classLoader;
} else {
this.serverClassLoader = new RedkaleClassLoader(this.classLoader);
}
}
private void loadResourceProperties() throws IOException {
final Properties dyncProps = new Properties();
final AtomicInteger propertyIndex = new AtomicInteger();
Properties logProps = null; //新的日志配置项
//------------------------------------ 读取本地DataSource、CacheSource配置 ------------------------------------
if ("file".equals(this.confPath.getScheme())) {
File sourceFile = new File(new File(confPath), "source.properties");
if (sourceFile.isFile() && sourceFile.canRead()) {
InputStream in = new FileInputStream(sourceFile);
Properties props = new Properties();
props.load(in);
in.close();
props.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
dyncProps.put(key, val);
} else {
logger.log(Level.WARNING, "skip illegal key " + key + " in source.properties");
}
});
} else {
//兼容 persistence.xml 【已废弃】
File persist = new File(new File(confPath), "persistence.xml");
if (persist.isFile() && persist.canRead()) {
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
InputStream in = new FileInputStream(persist);
dyncProps.putAll(DataSources.loadSourceProperties(in));
in.close();
}
}
} else { //从url或jar文件中resources读取
try {
final URI sourceURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), "source.properties");
InputStream in = sourceURI.toURL().openStream();
Properties props = new Properties();
props.load(in);
in.close();
props.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
dyncProps.put(key, val);
} else {
logger.log(Level.WARNING, "skip illegal key " + key + " in source.properties");
}
});
} catch (Exception e) { //没有文件 跳过
}
//兼容 persistence.xml 【已废弃】
try {
final URI xmlURI = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), "persistence.xml");
InputStream in = xmlURI.toURL().openStream();
dyncProps.putAll(DataSources.loadSourceProperties(in));
in.close();
logger.log(Level.WARNING, "persistence.xml is deprecated, replaced by source.properties");
} catch (Exception e) { //没有文件 跳过
}
}
//------------------------------------ 读取配置项 ------------------------------------
AnyValue propertiesConf = config.getAnyValue("properties");
if (propertiesConf == null) {
final AnyValue resources = config.getAnyValue("resources");
if (resources != null) {
logger.log(Level.WARNING, "<resources> in application config file is deprecated");
propertiesConf = resources.getAnyValue("properties");
}
}
if (propertiesConf != null) {
final Properties agentEnvs = new Properties();
if (propertiesConf.getValue("load") != null) { //本地配置项文件加载
for (String dfload : propertiesConf.getValue("load").split(";")) {
if (dfload.trim().isEmpty()) {
continue;
}
final URI df = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : this.confPath.toString(), dfload.trim());
if (df != null && (!"file".equals(df.getScheme()) || df.toString().contains("!") || new File(df).isFile())) {
Properties ps = new Properties();
try {
InputStream in = df.toURL().openStream();
ps.load(in);
in.close();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Load properties(" + dfload + ") size = " + ps.size());
}
ps.forEach((x, y) -> { //load中的配置项除了redkale.cachesource.和redkale.datasource.开头不应该有其他redkale.开头配置项
if (!x.toString().startsWith("redkale.")) {
agentEnvs.put(x, y);
} else {
logger.log(Level.WARNING, "skip illegal(startswith 'redkale.') key " + x + " in properties file");
}
});
} catch (Exception e) {
logger.log(Level.WARNING, "Load properties(" + dfload + ") error", e);
}
}
}
}
{ //可能通过系统环境变量配置信息
Iterator<PropertiesAgentProvider> it = ServiceLoader.load(PropertiesAgentProvider.class, classLoader).iterator();
RedkaleClassLoader.putServiceLoader(PropertiesAgentProvider.class);
List<PropertiesAgentProvider> providers = new ArrayList<>();
while (it.hasNext()) {
PropertiesAgentProvider provider = it.next();
if (provider != null && provider.acceptsConf(propertiesConf)) {
RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName());
providers.add(provider);
}
}
for (PropertiesAgentProvider provider : InstanceProvider.sort(providers)) {
long s = System.currentTimeMillis();
this.propertiesAgent = provider.createInstance();
this.resourceFactory.inject(this.propertiesAgent);
if (compileMode) {
this.propertiesAgent.compile(propertiesConf);
} else {
Map<String, Properties> propMap = this.propertiesAgent.init(this, propertiesConf);
int propCount = 0;
if (propMap != null) {
for (Map.Entry<String, Properties> en : propMap.entrySet()) {
propCount += en.getValue().size();
if (en.getKey().startsWith("logging")) {
if (logProps != null) {
logger.log(Level.WARNING, "skip repeat logging config properties(" + en.getKey() + ")");
} else {
logProps = en.getValue();
}
} else {
agentEnvs.putAll(en.getValue());
}
}
}
logger.info("PropertiesAgent (type = " + this.propertiesAgent.getClass().getSimpleName() + ") load " + propCount + " data in " + (System.currentTimeMillis() - s) + " ms");
}
break;
}
}
final Properties oldEnvs = new Properties();
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null || value == null) {
continue;
}
oldEnvs.put(key, value);
}
agentEnvs.forEach((k, v) -> {
if (k.toString().startsWith("redkale.")) {
dyncProps.put(k, v);
} else {
oldEnvs.put(k, v); //新配置项会覆盖旧的
}
});
//原有properties节点上的属性同步到dyncEnvs
propertiesConf.forEach((k, v) -> dyncProps.put("redkale.properties[" + k + "]", v));
oldEnvs.forEach((k, v) -> { //去重后的配置项
String prefix = "redkale.properties.property[" + propertyIndex.getAndIncrement() + "]";
dyncProps.put(prefix + ".name", k);
dyncProps.put(prefix + ".value", v);
});
//移除旧节点
((DefaultAnyValue) this.config).removeAnyValues("properties");
}
//环境变量的优先级最高
System.getProperties().forEach((k, v) -> {
if (k.toString().startsWith("redkale.executor.") //节点全局唯一
|| k.toString().startsWith("redkale.transport.") //节点全局唯一
|| k.toString().startsWith("redkale.excludelibs.") //节点全局唯一
|| k.toString().startsWith("redkale.cluster.") //节点全局唯一
|| k.toString().startsWith("redkale.mq.")
|| k.toString().startsWith("redkale.mq[")
|| k.toString().startsWith("redkale.group.")
|| k.toString().startsWith("redkale.group[")
|| k.toString().startsWith("redkale.listener.")
|| k.toString().startsWith("redkale.listener[")
|| k.toString().startsWith("redkale.server.")
|| k.toString().startsWith("redkale.server[")) {
dyncProps.put(k, v);
} else if (k.toString().startsWith("redkale.properties.")) {
if (k.toString().startsWith("redkale.properties.property.")
|| k.toString().startsWith("redkale.properties.property[")) {
dyncProps.put(k, v);
} else {
//支持系统变量 -Dredkale.properties.mykey=my-value
String prefix = "redkale.properties.property[" + propertyIndex.getAndIncrement() + "]";
dyncProps.put(prefix + ".name", k.toString().substring("redkale.properties.".length()));
dyncProps.put(prefix + ".value", v);
}
}
});
if (!dyncProps.isEmpty()) {
//合并配置
this.config.merge(AnyValue.loadFromProperties(dyncProps).getAnyValue("redkale"), NodeServer.appConfigmergeFunction);
dyncProps.forEach((key, val) -> {
if (key.toString().startsWith("redkale.datasource.") || key.toString().startsWith("redkale.datasource[")
|| key.toString().startsWith("redkale.cachesource.") || key.toString().startsWith("redkale.cachesource[")) {
if (key.toString().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + key + " in source config, key cannot endsWith '.name'");
} else {
this.sourceProperties.put(key, val);
}
} else if (key.toString().startsWith("redkale.mq.") || key.toString().startsWith("redkale.mq[")) {
if (key.toString().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + key + " in mq config, key cannot endsWith '.name'");
} else {
this.messageProperties.put(key, val);
}
} else if (key.toString().startsWith("redkale.cluster.")) {
this.clusterProperties.put(key, val);
}
});
}
//使用合并后的新配置节点
propertiesConf = this.config.getAnyValue("properties");
if (propertiesConf != null) {
//清除property节点数组的下坐标
((DefaultAnyValue) propertiesConf).clearParentArrayIndex("property");
//注入配置项
for (AnyValue prop : propertiesConf.getAnyValues("property")) {
String key = prop.getValue("name");
String value = prop.getValue("value");
if (key == null) {
continue;
}
value = value == null ? value : replaceValue(value);
if (key.startsWith("system.property.")) {
String propName = key.substring("system.property.".length());
if (System.getProperty(propName) == null) { //命令行传参数优先级高
System.setProperty(propName, value);
}
} else if (key.startsWith("mimetype.property.")) {
MimeType.add(key.substring("mimetype.property.".length()), value);
} else {
this.envProperties.put(key, value);
resourceFactory.register(false, key, value);
}
}
}
//重置日志配置
if (logProps != null && !logProps.isEmpty()) {
reconfigLogging(logProps);
}
}
void reconfigLogging(Properties properties0) {
String searchRawHandler = "java.util.logging.SearchHandler";
String searchReadHandler = LoggingSearchHandler.class.getName();
Properties properties = new Properties();
properties0.entrySet().forEach(x -> {
properties.put(x.getKey().toString().replace(searchRawHandler, searchReadHandler),
x.getValue().toString()
.replace("%m", "%tY%tm").replace("%d", "%tY%tm%td") //兼容旧时间格式
.replace("${" + RESNAME_APP_NAME + "}", getName())
.replace("${" + RESNAME_APP_HOME + "}", getHome().getPath().replace('\\', '/'))
.replace(searchRawHandler, searchReadHandler)
);
});
if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) {
if (compileMode) {
properties.setProperty("java.util.logging.FileHandler.formatter", SimpleFormatter.class.getName());
if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) {
properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n"));
}
} else {
properties.setProperty("java.util.logging.FileHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName());
}
}
if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) {
if (compileMode) {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", SimpleFormatter.class.getName());
if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) {
properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n"));
}
} else {
properties.setProperty("java.util.logging.ConsoleHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName());
}
}
if (!compileMode) { //ConsoleHandler替换成LoggingConsoleHandler
final String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.ConsoleHandler")) {
final String consoleHandlerClass = LoggingFileHandler.LoggingConsoleHandler.class.getName();
properties.setProperty("handlers", handlers.replace("java.util.logging.ConsoleHandler", consoleHandlerClass));
Properties prop = new Properties();
String prefix = consoleHandlerClass + ".";
properties.entrySet().forEach(x -> {
if (x.getKey().toString().startsWith("java.util.logging.ConsoleHandler.")) {
prop.put(x.getKey().toString().replace("java.util.logging.ConsoleHandler.", prefix), x.getValue());
}
});
prop.entrySet().forEach(x -> {
properties.put(x.getKey(), x.getValue());
});
}
}
String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern");
if (fileHandlerPattern != null && fileHandlerPattern.contains("%")) { //带日期格式
final String fileHandlerClass = LoggingFileHandler.class.getName();
Properties prop = new Properties();
final String handlers = properties.getProperty("handlers");
if (handlers != null && handlers.contains("java.util.logging.FileHandler")) {
//singletonrun模式下不输出文件日志
prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonMode || compileMode ? "" : fileHandlerClass));
}
if (!prop.isEmpty()) {
String prefix = fileHandlerClass + ".";
properties.entrySet().forEach(x -> {
if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) {
prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue());
}
});
prop.entrySet().forEach(x -> {
properties.put(x.getKey(), x.getValue());
});
}
if (!compileMode) {
properties.put(SncpClient.class.getSimpleName() + ".handlers", LoggingFileHandler.LoggingSncpFileHandler.class.getName());
}
}
if (compileMode) {
properties.put("handlers", "java.util.logging.ConsoleHandler");
Map newprop = new HashMap(properties);
newprop.forEach((k, v) -> {
if (k.toString().startsWith("java.util.logging.FileHandler.")) {
properties.remove(k);
}
});
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(out);
properties.forEach((x, y) -> ps.println(x + "=" + y));
try {
LogManager manager = LogManager.getLogManager();
manager.readConfiguration(new ByteArrayInputStream(out.toByteArray()));
this.loggingProperties.clear();
this.loggingProperties.putAll(properties);
Enumeration<String> en = manager.getLoggerNames();
while (en.hasMoreElements()) {
for (Handler handler : manager.getLogger(en.nextElement()).getHandlers()) {
if (handler instanceof LoggingSearchHandler) {
((LoggingSearchHandler) handler).application = this;
}
}
}
} catch (IOException e) { //不会发生
}
}
private static String colorMessage(Logger logger, int color, int type, String msg) {
final boolean linux = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("linux");
if (linux) { //Windows PowerShell 也能正常着色
boolean supported = true;
Logger l = logger;
do {
if (l.getHandlers() == null) {
break;
}
for (Handler handler : l.getHandlers()) {
if (!(handler instanceof ConsoleHandler)) {
supported = false;
break;
}
}
if (!supported) {
break;
}
} while ((l = l.getParent()) != null);
//colour 颜色代号:背景颜色代号(41-46);前景色代号(31-36)
//type 样式代号0无1加粗3斜体4下划线
//String.format("\033[%d;%dm%s\033[0m", colour, type, content)
if (supported) {
msg = "\033[" + color + (type > 0 ? (";" + type) : "") + "m" + msg + "\033[0m";
}
}
return msg;
}
private String checkName(String name) { //不能含特殊字符
if (name == null || name.isEmpty()) {
return name;
}
for (char ch : name.toCharArray()) {
if (!((ch >= '0' && ch <= '9') || ch == '_' || ch == '.' || ch == '-' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符
throw new RedkaleException("name only 0-9 a-z A-Z _ - . cannot begin 0-9");
}
}
return name;
}
public void init() throws Exception {
//只有WatchService才能加载Application、WatchFactory
final Application application = this;
this.resourceFactory.register(new ResourceTypeLoader() {
@Override
public Object load(ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) {
try {
String resName = null;
Resource res = field.getAnnotation(Resource.class);
if (res != null) {
resName = res.name();
} else {
javax.annotation.Resource res2 = field.getAnnotation(javax.annotation.Resource.class);
if (res2 != null) {
resName = res2.name();
}
}
if (resName == null) {
return null;
}
if (srcObj instanceof Service && Sncp.isRemote((Service) srcObj)) {
return null; //远程模式不得注入
}
Class type = field.getType();
if (type == Application.class) {
field.set(srcObj, application);
return application;
} else if (type == ResourceFactory.class) {
boolean serv = RESNAME_SERVER_RESFACTORY.equals(resName) || resName.equalsIgnoreCase("server");
ResourceFactory rs = serv ? rf : (resName.isEmpty() ? application.resourceFactory : null);
field.set(srcObj, rs);
return rs;
} else if (type == NodeSncpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() != NodeSncpServer.class) {
continue;
}
if (resName.equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(srcObj, server);
return server;
} else if (type == NodeHttpServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() != NodeHttpServer.class) {
continue;
}
if (resName.equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(srcObj, server);
return server;
} else if (type == NodeWatchServer.class) {
NodeServer server = null;
for (NodeServer ns : application.getNodeServers()) {
if (ns.getClass() != NodeWatchServer.class) {
continue;
}
if (resName.equals(ns.server.getName())) {
server = ns;
break;
}
}
field.set(srcObj, server);
return server;
}
// if (type == WatchFactory.class) {
// field.setex(src, application.watchFactory);
// }
return null;
} catch (Exception e) {
logger.log(Level.SEVERE, "Resource inject error", e);
return null;
}
}
@Override
public boolean autoNone() {
return false;
}
}, Application.class, ResourceFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class);
//------------------------------------ 注册 java.net.http.HttpClient ------------------------------------
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null && field.getAnnotation(javax.annotation.Resource.class) == null) {
return null;
}
java.net.http.HttpClient.Builder builder = java.net.http.HttpClient.newBuilder();
if (resourceName.endsWith(".1.1")) {
builder.version(HttpClient.Version.HTTP_1_1);
} else if (resourceName.endsWith(".2")) {
builder.version(HttpClient.Version.HTTP_2);
}
java.net.http.HttpClient httpClient = builder.build();
field.set(srcObj, httpClient);
rf.inject(resourceName, httpClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, java.net.http.HttpClient.class, httpClient);
return httpClient;
} catch (Exception e) {
logger.log(Level.SEVERE, "java.net.http.HttpClient inject error", e);
return null;
}
}, java.net.http.HttpClient.class);
//------------------------------------ 注册 HttpSimpleClient ------------------------------------
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null && field.getAnnotation(javax.annotation.Resource.class) == null) {
return null;
}
HttpSimpleClient httpClient = HttpSimpleClient.create(clientAsyncGroup);
field.set(srcObj, httpClient);
rf.inject(resourceName, httpClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpSimpleClient.class, httpClient);
return httpClient;
} catch (Exception e) {
logger.log(Level.SEVERE, "HttpClient inject error", e);
return null;
}
}, HttpSimpleClient.class);
//--------------------------------------------------------------------------
if (this.clientAsyncGroup != null) {
((AsyncIOGroup) this.clientAsyncGroup).start();
}
if (this.clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "ClusterAgent (type = " + this.clusterAgent.getClass().getSimpleName() + ") initing");
}
long s = System.currentTimeMillis();
if (this.clusterAgent instanceof CacheClusterAgent) {
String sourceName = ((CacheClusterAgent) clusterAgent).getSourceName(); //必须在inject前调用需要赋值Resourcable.name
loadCacheSource(sourceName, false);
}
this.resourceFactory.inject(clusterAgent);
clusterAgent.init(this.resourceFactory, clusterAgent.getConfig());
this.resourceFactory.register(ClusterAgent.class, clusterAgent);
logger.info("ClusterAgent (type = " + this.clusterAgent.getClass().getSimpleName() + ") init in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "MessageAgent initing");
}
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
this.resourceFactory.inject(agent);
agent.init(this.resourceFactory, agent.getConfig());
this.resourceFactory.register(agent.getName(), MessageAgent.class, agent);
this.resourceFactory.register(agent.getName(), HttpMessageClient.class, agent.getHttpMessageClient());
//this.resourceFactory.register(agent.getName(), SncpMessageClient.class, agent.getSncpMessageClient()); //不需要给开发者使用
}
logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms");
}
//------------------------------------ 注册 HttpMessageClient ------------------------------------
resourceFactory.register((ResourceFactory rf, String srcResourceName, final Object srcObj, String resourceName, Field field, final Object attachment) -> {
try {
if (field.getAnnotation(Resource.class) == null && field.getAnnotation(javax.annotation.Resource.class) == null) {
return null;
}
if (clusterAgent == null) {
HttpMessageClient messageClient = new HttpMessageLocalClient(application, resourceName);
field.set(srcObj, messageClient);
rf.inject(resourceName, messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
return messageClient;
}
HttpMessageClient messageClient = new HttpMessageClusterClient(application, resourceName, clusterAgent);
field.set(srcObj, messageClient);
rf.inject(resourceName, messageClient, null); // 给其可能包含@Resource的字段赋值;
rf.register(resourceName, HttpMessageClient.class, messageClient);
return messageClient;
} catch (Exception e) {
logger.log(Level.SEVERE, "HttpMessageClient inject error", e);
return null;
}
}, HttpMessageClient.class);
initResources();
}
private AnyValue findSourceConfig(String sourceName, String sourceType) {
AnyValue sourceNode = config.getAnyValue(sourceType);
if (sourceNode != null) {
AnyValue confNode = sourceNode.getAnyValue(sourceName);
if (confNode != null) { //必须要设置name属性
((DefaultAnyValue) confNode).setValue("name", sourceName);
}
return confNode;
}
return null;
}
private AnyValue findMQConfig(String mqName) {
AnyValue mqsNode = config.getAnyValue("mq");
if (mqsNode != null) {
AnyValue confNode = mqsNode.getAnyValue(mqName);
if (confNode != null) { //必须要设置name属性
((DefaultAnyValue) confNode).setValue("name", mqName);
}
return confNode;
}
return null;
}
CacheSource loadCacheSource(final String sourceName, boolean autoMemory) {
cacheSourceLock.lock();
try {
long st = System.currentTimeMillis();
CacheSource old = resourceFactory.find(sourceName, CacheSource.class);
if (old != null) {
return old;
}
final AnyValue sourceConf = findSourceConfig(sourceName, "cachesource");
if (sourceConf == null) {
if (!autoMemory) {
return null;
}
CacheSource source = new CacheMemorySource(sourceName);
cacheSources.add(source);
resourceFactory.register(sourceName, CacheSource.class, source);
if (!compileMode && source instanceof Service) {
((Service) source).init(sourceConf);
}
logger.info("Load CacheSource resourceName = '" + sourceName + "', source = " + source + " in " + (System.currentTimeMillis() - st) + " ms");
return source;
}
if (!sourceConf.getValue(AbstractCacheSource.CACHE_SOURCE_RESOURCE, "").isEmpty()) {
CacheSource source = loadCacheSource(sourceConf.getValue(AbstractCacheSource.CACHE_SOURCE_RESOURCE), autoMemory);
if (source != null) {
resourceFactory.register(sourceName, CacheSource.class, source);
}
return source;
}
try {
CacheSource source = AbstractCacheSource.createCacheSource(serverClassLoader, resourceFactory, sourceConf, sourceName, compileMode);
cacheSources.add(source);
resourceFactory.register(sourceName, CacheSource.class, source);
logger.info("Load CacheSource resourceName = '" + sourceName + "', source = " + source + " in " + (System.currentTimeMillis() - st) + " ms");
return source;
} catch (RuntimeException ex) {
throw ex;
} catch (Exception e) {
logger.log(Level.SEVERE, "load application CaheSource error: " + sourceConf, e);
}
return null;
} finally {
cacheSourceLock.unlock();
}
}
DataSource loadDataSource(final String sourceName, boolean autoMemory) {
dataSourceLock.lock();
try {
DataSource old = resourceFactory.find(sourceName, DataSource.class);
if (old != null) {
return old;
}
final AnyValue sourceConf = findSourceConfig(sourceName, "datasource");
if (sourceConf == null) {
if (!autoMemory) {
return null;
}
DataSource source = new DataMemorySource(sourceName);
if (!compileMode && source instanceof Service) {
resourceFactory.inject(sourceName, source);
((Service) source).init(sourceConf);
}
dataSources.add(source);
resourceFactory.register(sourceName, DataSource.class, source);
logger.info("Load DataSource resourceName = '" + sourceName + "', source = " + source);
return source;
}
if (!sourceConf.getValue(AbstractDataSource.DATA_SOURCE_RESOURCE, "").isEmpty()) {
DataSource source = loadDataSource(sourceConf.getValue(AbstractDataSource.DATA_SOURCE_RESOURCE), autoMemory);
if (source != null) {
if (source instanceof DataMemorySource && DataMemorySource.isSearchType(sourceConf)) {
resourceFactory.register(sourceName, SearchSource.class, source);
} else {
resourceFactory.register(sourceName, DataSource.class, source);
}
}
return source;
}
try {
DataSource source = AbstractDataSource.createDataSource(serverClassLoader, resourceFactory, sourceConf, sourceName, compileMode);
dataSources.add(source);
if (source instanceof DataMemorySource && DataMemorySource.isSearchType(sourceConf)) {
resourceFactory.register(sourceName, SearchSource.class, source);
} else {
resourceFactory.register(sourceName, DataSource.class, source);
}
logger.info("Load DataSource resourceName = '" + sourceName + "', source = " + source);
return source;
} catch (RuntimeException ex) {
throw ex;
} catch (Exception e) {
logger.log(Level.SEVERE, "load application DataSource error: " + sourceConf, e);
}
return null;
} finally {
dataSourceLock.unlock();
}
}
private void initResources() throws Exception {
//------------------------------------------------------------------------
for (AnyValue conf : config.getAnyValues("group")) {
final String group = conf.getValue("name", "");
if (group.indexOf('$') >= 0) {
throw new RedkaleException("<group> name cannot contains '$' in " + group);
}
final String protocol = conf.getValue("protocol", "TCP").toUpperCase();
if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) {
throw new RedkaleException("Not supported Transport Protocol " + conf.getValue("protocol"));
}
SncpRpcGroup rg = sncpRpcGroups.computeIfAbsent(group, protocol);
for (AnyValue node : conf.getAnyValues("node")) {
rg.putAddress(new InetSocketAddress(node.getValue("addr"), node.getIntValue("port")));
}
}
for (AnyValue conf : config.getAnyValues("listener")) {
final String listenClass = conf.getValue("value", "");
if (listenClass.isEmpty()) {
continue;
}
Class clazz = classLoader.loadClass(listenClass);
if (!ApplicationListener.class.isAssignableFrom(clazz)) {
continue;
}
RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName());
@SuppressWarnings("unchecked")
ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance();
resourceFactory.inject(listener);
listener.init(config);
this.listeners.add(listener);
}
//------------------------------------------------------------------------
}
private void startSelfServer() throws Exception {
if (config.getValue("port", "").isEmpty() || "0".equals(config.getValue("port"))) {
return; //没有配置port则不启动进程自身的监听
}
final Application application = this;
new Thread() {
{
setName("Redkale-Application-SelfServer-Thread");
}
@Override
public void run() {
try {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.socket().setSoTimeout(6000); //单位:毫秒
channel.bind(new InetSocketAddress("127.0.0.1", config.getIntValue("port")));
if (!singletonMode) {
signalShutdownHandle();
}
boolean loop = true;
final ByteBuffer buffer = ByteBuffer.allocateDirect(UDP_CAPACITY);
while (loop) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
SocketAddress address = readUdpData(channel, buffer, out);
String[] args = JsonConvert.root().convertFrom(String[].class, out.toString(StandardCharsets.UTF_8));
final String cmd = args[0];
String[] params = args.length == 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length);
//接收到命令必须要有回应, 无结果输出则回应回车换行符
if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
try {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " shutdowning");
application.shutdown();
sendUdpData(channel, address, buffer, "--- shutdown finish ---".getBytes(StandardCharsets.UTF_8));
long e = System.currentTimeMillis() - s;
logger.info(application.getClass().getSimpleName() + " shutdown in " + e + " ms");
channel.close();
} catch (Exception ex) {
logger.log(Level.INFO, "Shutdown fail", ex);
sendUdpData(channel, address, buffer, "shutdown fail".getBytes(StandardCharsets.UTF_8));
} finally {
loop = false;
application.shutdownLatch.countDown();
}
} else if ("APIDOC".equalsIgnoreCase(cmd)) {
try {
String rs = new ApiDocCommand(application).command(cmd, params);
if (rs == null || rs.isEmpty()) {
rs = "\r\n";
}
sendUdpData(channel, address, buffer, rs.getBytes(StandardCharsets.UTF_8));
} catch (Exception ex) {
sendUdpData(channel, address, buffer, "apidoc fail".getBytes(StandardCharsets.UTF_8));
}
} else {
long s = System.currentTimeMillis();
logger.info(application.getClass().getSimpleName() + " command " + cmd);
List<Object> rs = application.command(cmd, params);
StringBuilder sb = new StringBuilder();
if (rs != null) {
for (Object o : rs) {
if (o instanceof CharSequence) {
sb.append(o).append("\r\n");
} else {
sb.append(JsonConvert.root().convertTo(o)).append("\r\n");
}
}
}
if (sb.length() == 0) {
sb.append("\r\n");
}
sendUdpData(channel, address, buffer, sb.toString().getBytes(StandardCharsets.UTF_8));
long e = System.currentTimeMillis() - s;
logger.info(application.getClass().getSimpleName() + " command in " + e + " ms");
}
}
} catch (Exception e) {
logger.log(Level.INFO, "Control fail", e);
System.exit(1);
}
}
}.start();
}
//数据包前4个字节为数据内容的长度
private static void sendUdpData(final DatagramChannel channel, SocketAddress dest, ByteBuffer buffer, byte[] bytes) throws IOException {
buffer.clear();
int count = (bytes.length + 4) / UDP_CAPACITY + ((bytes.length + 4) % UDP_CAPACITY > 0 ? 1 : 0);
int start = 0;
for (int i = 0; i < count; i++) {
if (start == 0) {
buffer.putInt(bytes.length);
}
int len = Math.min(buffer.remaining(), bytes.length - start);
buffer.put(bytes, start, len);
buffer.flip();
boolean first = true;
while (buffer.hasRemaining()) {
if (!first) {
Utility.sleep(10);
}
if (dest == null) {
channel.write(buffer);
} else {
channel.send(buffer, dest);
}
first = false;
}
start += len;
buffer.clear();
}
}
private static SocketAddress readUdpData(final DatagramChannel channel, ByteBuffer buffer, ByteArrayOutputStream out) throws IOException {
out.reset();
buffer.clear();
SocketAddress src = null;
if (channel.isConnected()) {
channel.read(buffer);
while (buffer.position() < 4) channel.read(buffer);
} else {
src = channel.receive(buffer);
while (buffer.position() < 4) src = channel.receive(buffer);
}
buffer.flip();
final int dataSize = buffer.getInt();
while (buffer.hasRemaining()) {
out.write(buffer.get());
}
while (out.size() < dataSize) {
buffer.clear();
channel.receive(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
out.write(buffer.get());
}
}
return src;
}
private static void sendCommand(Logger logger, int port, String cmd, String[] params) throws Exception {
final DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(true);
channel.socket().setSoTimeout(6000); //单位:毫秒
SocketAddress dest = new InetSocketAddress("127.0.0.1", port);
channel.connect(dest);
//命令和参数合成一个数组
String[] args = new String[1 + (params == null ? 0 : params.length)];
args[0] = cmd;
if (params != null) {
System.arraycopy(params, 0, args, 1, params.length);
}
final ByteBuffer buffer = ByteBuffer.allocateDirect(UDP_CAPACITY);
try {
sendUdpData(channel, dest, buffer, JsonConvert.root().convertToBytes(args));
ByteArrayOutputStream out = new ByteArrayOutputStream();
readUdpData(channel, buffer, out);
channel.close();
String rs = out.toString(StandardCharsets.UTF_8).trim();
if (logger != null) {
logger.info("Send: " + cmd + ", Reply: " + rs);
}
System.out.println(rs);
} catch (Exception e) {
if (e instanceof PortUnreachableException) {
if ("APIDOC".equalsIgnoreCase(cmd)) {
final Application application = new Application(false, true, Application.loadAppConfig());
application.init();
application.start();
String rs = new ApiDocCommand(application).command(cmd, params);
application.shutdown();
if (logger != null) {
logger.info(rs);
}
System.out.println(rs);
return;
}
//if ("SHUTDOWN".equalsIgnoreCase(cmd)) {
// System.out.println("--- application not running ---");
//} else {
System.err.println("--- application not running ---");
//}
return;
}
throw e;
}
}
/**
* 启动
*
* @throws Exception 异常
*/
public void start() throws Exception {
if (!singletonMode && !compileMode && this.clusterAgent != null) {
this.clusterAgent.register(this);
}
final AnyValue[] entrys = config.getAnyValues("server");
CountDownLatch timecd = new CountDownLatch(entrys.length);
final List<AnyValue> sncps = new ArrayList<>();
final List<AnyValue> others = new ArrayList<>();
final List<AnyValue> watchs = new ArrayList<>();
for (final AnyValue entry : entrys) {
if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) {
sncps.add(entry);
} else if (entry.getValue("protocol", "").toUpperCase().startsWith("WATCH")) {
watchs.add(entry);
} else {
others.add(entry);
}
}
if (watchs.size() > 1) {
throw new RedkaleException("Found one more WATCH Server");
}
this.watching = !watchs.isEmpty();
runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务
runServers(timecd, others);
runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务
timecd.await();
if (this.clusterAgent != null) {
this.clusterAgent.start();
}
if (this.messageAgents != null) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "MessageAgent starting");
}
long s = System.currentTimeMillis();
final StringBuffer sb = new StringBuffer();
Set<String> names = new HashSet<>();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
Map<String, Long> map = agent.start().join();
AtomicInteger maxlen = new AtomicInteger();
map.keySet().forEach(str -> {
if (str.length() > maxlen.get()) {
maxlen.set(str.length());
}
});
new TreeMap<>(map).forEach((topic, ms) -> sb.append("MessageConsumer(topic=").append(alignString(topic, maxlen.get())).append(") init and start in ").append(ms).append(" ms\r\n")
);
}
if (sb.length() > 0) {
logger.info(sb.toString().trim());
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") start in " + (System.currentTimeMillis() - s) + " ms");
}
for (ApplicationListener listener : this.listeners) {
listener.postStart(this);
}
long intms = System.currentTimeMillis() - startTime;
String ms = String.valueOf(intms);
int repeat = ms.length() > 7 ? 0 : (7 - ms.length()) / 2;
logger.info(colorMessage(logger, 36, 1, "-".repeat(repeat) + "------------------------ Redkale started in " + ms + " ms " + (ms.length() / 2 == 0 ? " " : "") + "-".repeat(repeat) + "------------------------") + "\r\n");
LoggingBaseHandler.traceFlag = true;
if (!singletonMode && !compileMode) {
this.shutdownLatch.await();
}
}
private static String alignString(String value, int maxlen) {
StringBuilder sb = new StringBuilder(maxlen);
sb.append(value);
for (int i = 0; i < maxlen - value.length(); i++) {
sb.append(' ');
}
return sb.toString();
}
//使用了nohup或使用了后台&Runtime.getRuntime().addShutdownHook失效
private void signalShutdownHandle() {
Consumer<Consumer<String>> signalShutdownConsumer = Utility.signalShutdownConsumer();
if (signalShutdownConsumer == null) {
return;
}
signalShutdownConsumer.accept(sig -> {
try {
long s = System.currentTimeMillis();
logger.info(Application.this.getClass().getSimpleName() + " shutdowning " + sig);
shutdown();
long e = System.currentTimeMillis() - s;
logger.info(Application.this.getClass().getSimpleName() + " shutdown in " + e + " ms");
} catch (Exception ex) {
logger.log(Level.INFO, "Shutdown fail", ex);
} finally {
shutdownLatch.countDown();
}
});
}
@SuppressWarnings("unchecked")
private void runServers(CountDownLatch timecd, final List<AnyValue> serconfs) throws Exception {
this.servicecdl = new CountDownLatch(serconfs.size());
CountDownLatch sercdl = new CountDownLatch(serconfs.size());
final AtomicBoolean inited = new AtomicBoolean(false);
final ReentrantLock nodeLock = new ReentrantLock();
final Map<String, Class<? extends NodeServer>> nodeClasses = new HashMap<>();
for (final AnyValue serconf : serconfs) {
Thread thread = new Thread() {
{
setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread");
this.setDaemon(true);
}
@Override
public void run() {
try {
//Thread ctd = Thread.currentThread();
//ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader()));
final String protocol = serconf.getValue("protocol", "").replaceFirst("\\..+", "").toUpperCase();
NodeServer server = null;
if ("SNCP".equals(protocol)) {
server = NodeSncpServer.createNodeServer(Application.this, serconf);
} else if ("WATCH".equalsIgnoreCase(protocol)) {
DefaultAnyValue serconf2 = (DefaultAnyValue) serconf;
DefaultAnyValue rest = (DefaultAnyValue) serconf2.getAnyValue("rest");
if (rest == null) {
rest = new DefaultAnyValue();
serconf2.addValue("rest", rest);
}
rest.setValue("base", WatchServlet.class.getName());
server = new NodeWatchServer(Application.this, serconf);
} else if ("HTTP".equalsIgnoreCase(protocol)) {
server = new NodeHttpServer(Application.this, serconf);
} else {
if (!inited.get()) {
nodeLock.lock();
try {
if (!inited.getAndSet(true)) { //加载自定义的协议SOCKS
ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null);
ClassFilter.Loader.load(home, classLoader, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter);
final Set<FilterEntry<NodeServer>> entrys = profilter.getFilterEntrys();
for (FilterEntry<NodeServer> entry : entrys) {
final Class<? extends NodeServer> type = entry.getType();
NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
String p = pros.value().toUpperCase();
if ("SNCP".equals(p) || "HTTP".equals(p)) {
continue;
}
final Class<? extends NodeServer> old = nodeClasses.get(p);
if (old != null && old != type) {
throw new RedkaleException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")");
}
nodeClasses.put(p, type);
}
}
} finally {
nodeLock.unlock();
}
}
Class<? extends NodeServer> nodeClass = nodeClasses.get(protocol);
if (nodeClass != null) {
server = NodeServer.create(nodeClass, Application.this, serconf);
}
}
if (server == null) {
logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol"));
System.exit(1);
}
server.init(serconf);
if (!singletonMode && !compileMode) {
server.start();
} else if (compileMode) {
server.getServer().getDispatcherServlet().init(server.getServer().getContext(), serconf);
}
servers.add(server);
timecd.countDown();
sercdl.countDown();
} catch (Exception ex) {
logger.log(Level.WARNING, serconf + " runServers error", ex);
Application.this.shutdownLatch.countDown();
System.exit(1);
}
}
};
thread.start();
}
sercdl.await();
}
/**
* 实例化单个Service
*
* @param <T> 泛型
* @param serviceClass 指定的service类
* @param extServiceClasses 需要排除的service类
*
* @return Service对象
* @throws Exception 异常
*/
public static <T extends Service> T singleton(Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
return singleton("", serviceClass, extServiceClasses);
}
/**
* 实例化单个Service
*
* @param <T> 泛型
* @param name Service的资源名
* @param serviceClass 指定的service类
* @param extServiceClasses 需要排除的service类
*
* @return Service对象
* @throws Exception 异常
*/
public static <T extends Service> T singleton(String name, Class<T> serviceClass, Class<? extends Service>... extServiceClasses) throws Exception {
if (serviceClass == null) {
throw new IllegalArgumentException("serviceClass is null");
}
final Application application = Application.create(true);
System.setProperty("redkale.singleton.serviceclass", serviceClass.getName());
if (extServiceClasses != null && extServiceClasses.length > 0) {
StringBuilder sb = new StringBuilder();
for (Class clazz : extServiceClasses) {
if (sb.length() > 0) {
sb.append(',');
}
sb.append(clazz.getName());
}
System.setProperty("redkale.singleton.extserviceclasses", sb.toString());
}
application.init();
application.start();
for (NodeServer server : application.servers) {
T service = server.resourceFactory.find(name, serviceClass);
if (service != null) {
return service;
}
}
if (Modifier.isAbstract(serviceClass.getModifiers())) {
throw new IllegalArgumentException("abstract class not allowed");
}
if (serviceClass.isInterface()) {
throw new IllegalArgumentException("interface class not allowed");
}
throw new IllegalArgumentException(serviceClass.getName() + " maybe have zero not-final public method");
}
public static Application create(final boolean singleton) throws IOException {
return new Application(singleton, false, loadAppConfig());
}
static AnyValue loadAppConfig() throws IOException {
final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/');
System.setProperty(RESNAME_APP_HOME, home);
String sysConfFile = System.getProperty(RESNAME_APP_CONF_FILE);
if (sysConfFile != null) {
String text;
if (sysConfFile.contains("://")) {
text = Utility.readThenClose(URI.create(sysConfFile).toURL().openStream());
} else {
File f = new File(sysConfFile);
if (f.isFile() && f.canRead()) {
text = Utility.readThenClose(new FileInputStream(f));
} else {
throw new IOException("Read application conf file (" + sysConfFile + ") error ");
}
}
return text.trim().startsWith("<") ? AnyValue.loadFromXml(text, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application") : AnyValue.loadFromProperties(text).getAnyValue("redkale");
}
String confDir = System.getProperty(RESNAME_APP_CONF_DIR, "conf");
URI appConfFile;
boolean fromCache = false;
if (confDir.contains("://")) { //jar内部资源
appConfFile = URI.create(confDir + (confDir.endsWith("/") ? "" : "/") + "application.xml");
try {
appConfFile.toURL().openStream().close();
} catch (IOException e) { //没有application.xml就尝试读application.properties
appConfFile = URI.create(confDir + (confDir.endsWith("/") ? "" : "/") + "application.properties");
}
} else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) { //绝对路径
File f = new File(confDir, "application.xml");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
f = new File(confDir, "application.properties");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir
try {
appConfFile.toURL().openStream().close();
} catch (IOException e) { //没有application.xml就尝试读application.properties
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.properties");
}
confDir = appConfFile.toString().replace("/application.xml", "").replace("/application.properties", "");
fromCache = true;
}
}
} else { //相对路径
File f = new File(new File(home, confDir), "application.xml");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
f = new File(new File(home, confDir), "application.properties");
if (f.isFile() && f.canRead()) {
appConfFile = f.toURI();
confDir = f.getParentFile().getCanonicalPath();
} else {
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir
try {
appConfFile.toURL().openStream().close();
} catch (IOException e) { //没有application.xml就尝试读application.properties
appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.properties");
}
confDir = appConfFile.toString().replace("/application.xml", "").replace("/application.properties", "");
fromCache = true;
}
}
}
System.setProperty(RESNAME_APP_CONF_DIR, confDir);
String text = Utility.readThenClose(appConfFile.toURL().openStream());
AnyValue conf;
if (text.trim().startsWith("<")) {
conf = AnyValue.loadFromXml(text, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application");
} else {
conf = AnyValue.loadFromProperties(text).getAnyValue("redkale");
}
if (fromCache) {
((DefaultAnyValue) conf).addValue("[config-from-cache]", "true");
}
return conf;
}
public static void main(String[] args) throws Exception {
Utility.midnight(); //先初始化一下Utility
Thread.currentThread().setName("Redkale-Application-Main-Thread");
//运行主程序
{
String cmd = System.getProperty("cmd", System.getProperty("CMD"));
String[] params = args;
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null && args[i].toLowerCase().startsWith("--conf-file=")) {
System.setProperty(RESNAME_APP_CONF_FILE, args[i].substring("--conf-file=".length()));
String[] newargs = new String[args.length - 1];
System.arraycopy(args, 0, newargs, 0, i);
System.arraycopy(args, i + 1, newargs, i, args.length - 1 - i);
args = newargs;
break;
}
}
if (cmd == null) {
for (int i = 0; i < args.length; i++) {
if (args[i] != null && !args[i].startsWith("-")) { //非-开头的第一个视为命令号
cmd = args[i];
if ("start".equalsIgnoreCase(cmd) || "startup".equalsIgnoreCase(cmd)) {
cmd = null;
}
params = new String[args.length - 1];
System.arraycopy(args, 0, params, 0, i);
System.arraycopy(args, i + 1, params, i, args.length - 1 - i);
break;
}
}
}
if (cmd == null) {
if (args.length == 1 && ("--help".equalsIgnoreCase(args[0]) || "-h".equalsIgnoreCase(args[0]))) {
cmd = args[0];
}
}
}
if (cmd != null) {
if ("stop".equalsIgnoreCase(cmd)) {
cmd = "shutdown";
}
if ("help".equalsIgnoreCase(cmd) || "--help".equalsIgnoreCase(cmd) || "-h".equalsIgnoreCase(cmd)) {
System.out.println(generateHelp());
return;
}
boolean restart = "restart".equalsIgnoreCase(cmd);
AnyValue config = loadAppConfig();
Application.sendCommand(null, config.getIntValue("port"), restart ? "SHUTDOWN" : cmd, params);
if (!restart) {
return;
}
}
}
//PrepareCompiler.main(args); //测试代码
final Application application = Application.create(false);
application.init();
application.startSelfServer();
try {
for (ApplicationListener listener : application.listeners) {
listener.preStart(application);
}
application.start();
} catch (Exception e) {
application.logger.log(Level.SEVERE, "Application start error", e);
System.exit(1);
}
System.exit(0); //必须要有
}
private String replaceValue(String value) {
return value == null ? value : value.replace("${APP_HOME}", homePath).replace("${APP_NAME}", name);
}
void updateEnvironmentProperties(String namespace, List<ResourceEvent> events) {
if (events == null || events.isEmpty()) {
return;
}
envPropertiesLock.lock();
try {
Properties envRegisterProps = new Properties();
Set<String> envRemovedKeys = new HashSet<>();
Properties envChangedProps = new Properties();
Set<String> sourceRemovedKeys = new HashSet<>();
Properties sourceChangedProps = new Properties();
Set<String> loggingRemovedKeys = new HashSet<>();
Properties loggingChangedProps = new Properties();
Set<String> clusterRemovedKeys = new HashSet<>();
Properties clusterChangedProps = new Properties();
Set<String> messageRemovedKeys = new HashSet<>();
Properties messageChangedProps = new Properties();
for (ResourceEvent<String> event : events) {
if (namespace != null && namespace.startsWith("logging")) {
if (event.newValue() == null) {
if (this.loggingProperties.containsKey(event.name())) {
loggingRemovedKeys.add(event.name());
}
} else {
loggingChangedProps.put(event.name(), event.newValue());
}
continue;
}
if (event.name().startsWith("redkale.datasource.") || event.name().startsWith("redkale.datasource[")
|| event.name().startsWith("redkale.cachesource.") || event.name().startsWith("redkale.cachesource[")) {
if (event.name().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + event.name() + " in source config " + (namespace == null ? "" : namespace) + ", key cannot endsWith '.name'");
} else {
if (!Objects.equals(event.newValue(), this.sourceProperties.getProperty(event.name()))) {
if (event.newValue() == null) {
if (this.sourceProperties.containsKey(event.name())) {
sourceRemovedKeys.add(event.name());
}
} else {
sourceChangedProps.put(event.name(), event.newValue());
}
}
}
} else if (event.name().startsWith("redkale.mq.") || event.name().startsWith("redkale.mq[")) {
if (event.name().endsWith(".name")) {
logger.log(Level.WARNING, "skip illegal key " + event.name() + " in mq config " + (namespace == null ? "" : namespace) + ", key cannot endsWith '.name'");
} else {
if (!Objects.equals(event.newValue(), this.messageProperties.getProperty(event.name()))) {
if (event.newValue() == null) {
if (this.messageProperties.containsKey(event.name())) {
messageRemovedKeys.add(event.name());
}
} else {
messageChangedProps.put(event.name(), event.newValue());
}
}
}
} else if (event.name().startsWith("redkale.cluster.")) {
if (!Objects.equals(event.newValue(), this.clusterProperties.getProperty(event.name()))) {
if (event.newValue() == null) {
if (this.clusterProperties.containsKey(event.name())) {
clusterRemovedKeys.add(event.name());
}
} else {
clusterChangedProps.put(event.name(), event.newValue());
}
}
} else if (event.name().startsWith("system.property.")) {
String propName = event.name().substring("system.property.".length());
if (event.newValue() == null) {
System.getProperties().remove(propName);
} else {
System.setProperty(propName, event.newValue());
}
} else if (event.name().startsWith("mimetype.property.")) {
String propName = event.name().substring("system.property.".length());
if (event.newValue() != null) {
MimeType.add(propName, event.newValue());
}
} else if (event.name().startsWith("redkale.")) {
logger.log(Level.WARNING, "not support the environment property key " + event.name() + " on change event");
} else {
if (!Objects.equals(event.newValue(), this.envProperties.getProperty(event.name()))) {
envRegisterProps.put(event.name(), event.newValue());
if (event.newValue() == null) {
if (this.envProperties.containsKey(event.name())) {
envRemovedKeys.add(event.name());
}
} else {
envChangedProps.put(event.name(), event.newValue());
}
}
}
}
//普通配置项的变更
if (!envRegisterProps.isEmpty()) {
this.envProperties.putAll(envChangedProps);
envRemovedKeys.forEach(k -> this.envProperties.remove(k));
DefaultAnyValue oldConf = (DefaultAnyValue) this.config.getAnyValue("properties");
DefaultAnyValue newConf = new DefaultAnyValue();
oldConf.forEach((k, v) -> newConf.addValue(k, v));
this.envProperties.forEach((k, v) -> {
newConf.addValue("property", new DefaultAnyValue().addValue("name", k.toString()).addValue("value", v.toString()));
});
oldConf.replace(newConf);
resourceFactory.register(envRegisterProps, "", Environment.class);
}
//日志配置项的变更
if (!loggingChangedProps.isEmpty() || !loggingRemovedKeys.isEmpty()) {
//只是简单变更日志级别则直接操作,无需重新配置日志
if (loggingRemovedKeys.isEmpty() && loggingChangedProps.size() == 1 && loggingChangedProps.containsKey(".level")) {
try {
Level logLevel = Level.parse(loggingChangedProps.getProperty(".level"));
Logger.getGlobal().setLevel(logLevel);
this.loggingProperties.putAll(loggingChangedProps);
logger.log(Level.INFO, "Reconfig logging level to " + logLevel);
} catch (Exception e) {
logger.log(Level.WARNING, "Reconfig logging level error, new level is " + loggingChangedProps.getProperty(".level"));
}
} else {
Properties newLogProps = new Properties();
newLogProps.putAll(this.loggingProperties);
newLogProps.putAll(loggingChangedProps);
loggingRemovedKeys.forEach(k -> newLogProps.remove(k));
reconfigLogging(newLogProps);
logger.log(Level.INFO, "Reconfig logging finished ");
}
}
//数据源配置项的变更
if (!sourceChangedProps.isEmpty() || !sourceRemovedKeys.isEmpty()) {
Set<String> cacheSourceNames = new LinkedHashSet<>();
Set<String> dataSourceNames = new LinkedHashSet<>();
List<String> keys = new ArrayList<>();
keys.addAll(sourceRemovedKeys);
keys.addAll((Set) sourceChangedProps.keySet());
for (final String key : keys) {
if (key.startsWith("redkale.cachesource[")) {
cacheSourceNames.add(key.substring("redkale.cachesource[".length(), key.indexOf(']')));
} else if (key.startsWith("redkale.cachesource.")) {
cacheSourceNames.add(key.substring("redkale.cachesource.".length(), key.indexOf('.', "redkale.cachesource.".length())));
} else if (key.startsWith("redkale.datasource[")) {
dataSourceNames.add(key.substring("redkale.datasource[".length(), key.indexOf(']')));
} else if (key.startsWith("redkale.datasource.")) {
dataSourceNames.add(key.substring("redkale.datasource.".length(), key.indexOf('.', "redkale.datasource.".length())));
}
}
//更新缓存
for (String sourceName : cacheSourceNames) {
CacheSource source = Utility.find(cacheSources, s -> Objects.equals(s.resourceName(), sourceName));
if (source == null) {
continue; //多余的数据源
}
final DefaultAnyValue old = (DefaultAnyValue) findSourceConfig(sourceName, "cachesource");
Properties newProps = new Properties();
this.sourceProperties.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
});
List<ResourceEvent> changeEvents = new ArrayList<>();
sourceChangedProps.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), v, this.sourceProperties.getProperty(key)));
});
sourceRemovedKeys.forEach(k -> {
final String key = k;
String prefix = "redkale.cachesource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.cachesource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return;
}
newProps.remove(k); //不是同一name数据源配置项
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), null, this.sourceProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
try {
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("cachesource").getAnyValue(sourceName));
((AbstractCacheSource) source).onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
}
//更新数据库
for (String sourceName : dataSourceNames) {
DataSource source = Utility.find(dataSources, s -> Objects.equals(s.resourceName(), sourceName));
if (source == null) {
continue; //多余的数据源
}
DefaultAnyValue old = (DefaultAnyValue) findSourceConfig(sourceName, "datasource");
Properties newProps = new Properties();
this.sourceProperties.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
});
List<ResourceEvent> changeEvents = new ArrayList<>();
sourceChangedProps.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), v, this.sourceProperties.getProperty(key)));
});
sourceRemovedKeys.forEach(k -> {
final String key = k;
String prefix = "redkale.datasource[" + sourceName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.datasource." + sourceName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return;
}
newProps.remove(k); //不是同一name数据源配置项
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), null, this.sourceProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
try {
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("datasource").getAnyValue(sourceName));
((AbstractDataSource) source).onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
}
sourceRemovedKeys.forEach(k -> this.sourceProperties.remove(k));
this.sourceProperties.putAll(sourceChangedProps);
}
//MQ配置项的变更
if (!messageChangedProps.isEmpty() || !messageRemovedKeys.isEmpty()) {
Set<String> messageNames = new LinkedHashSet<>();
List<String> keys = new ArrayList<>();
keys.addAll(messageRemovedKeys);
keys.addAll((Set) messageChangedProps.keySet());
for (final String key : keys) {
if (key.startsWith("redkale.mq[")) {
messageNames.add(key.substring("redkale.mq[".length(), key.indexOf(']')));
} else if (key.startsWith("redkale.mq.")) {
messageNames.add(key.substring("redkale.mq.".length(), key.indexOf('.', "redkale.mq.".length())));
}
}
//更新MQ
for (String mqName : messageNames) {
MessageAgent agent = Utility.find(messageAgents, s -> Objects.equals(s.resourceName(), mqName));
if (agent == null) {
continue; //多余的数据源
}
final DefaultAnyValue old = (DefaultAnyValue) findMQConfig(mqName);
Properties newProps = new Properties();
this.messageProperties.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.mq[" + mqName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.mq." + mqName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
});
List<ResourceEvent> changeEvents = new ArrayList<>();
messageChangedProps.forEach((k, v) -> {
final String key = k.toString();
String prefix = "redkale.mq[" + mqName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.mq." + mqName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return; //不是同一name数据源配置项
}
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), v, this.messageProperties.getProperty(key)));
});
messageRemovedKeys.forEach(k -> {
final String key = k;
String prefix = "redkale.mq[" + mqName + "].";
int pos = key.indexOf(prefix);
if (pos < 0) {
prefix = "redkale.mq." + mqName + ".";
pos = key.indexOf(prefix);
}
if (pos < 0) {
return;
}
newProps.remove(k); //不是同一name数据源配置项
changeEvents.add(ResourceEvent.create(key.substring(prefix.length()), null, this.messageProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
try {
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("mq").getAnyValue(mqName));
agent.onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
}
messageRemovedKeys.forEach(k -> this.messageProperties.remove(k));
this.messageProperties.putAll(messageChangedProps);
}
//第三方服务注册配置项的变更
if (!clusterChangedProps.isEmpty() || !clusterRemovedKeys.isEmpty()) {
if (this.clusterAgent != null) {
final DefaultAnyValue old = (DefaultAnyValue) config.getAnyValue("cluster");
Properties newProps = new Properties();
newProps.putAll(clusterProperties);
List<ResourceEvent> changeEvents = new ArrayList<>();
clusterChangedProps.forEach((k, v) -> {
final String key = k.toString();
newProps.put(k, v);
changeEvents.add(ResourceEvent.create(key.substring("redkale.cluster.".length()), v, this.clusterProperties.getProperty(key)));
});
clusterRemovedKeys.forEach(k -> {
final String key = k;
newProps.remove(k);
changeEvents.add(ResourceEvent.create(key.substring("redkale.cluster.".length()), null, this.clusterProperties.getProperty(key)));
});
if (!changeEvents.isEmpty()) {
DefaultAnyValue back = old.copy();
try {
old.replace(AnyValue.loadFromProperties(newProps).getAnyValue("redkale").getAnyValue("cluster"));
clusterAgent.onResourceChange(changeEvents.toArray(new ResourceEvent[changeEvents.size()]));
} catch (RuntimeException e) {
old.replace(back); //还原配置
throw e;
}
}
} else {
StringBuilder sb = new StringBuilder();
clusterChangedProps.forEach((k, v) -> {
sb.append(ClusterAgent.class.getSimpleName()).append(" skip change '").append(k).append("'\r\n");
});
clusterRemovedKeys.forEach(k -> {
sb.append(ClusterAgent.class.getSimpleName()).append(" skip change '").append(k).append("'\r\n");
});
if (sb.length() > 0) {
logger.log(Level.INFO, sb.toString());
}
}
clusterRemovedKeys.forEach(k -> this.clusterProperties.remove(k));
this.clusterProperties.putAll(clusterChangedProps);
}
} finally {
envPropertiesLock.unlock();
}
}
private static String generateHelp() {
return ""
+ "Usage: redkale [command] [arguments]\r\n"
+ "Command: \r\n"
+ " start, startup start one process\r\n"
+ " --conf-file=[file] application config file, eg. application.xml、application.properties\r\n"
+ " shutdown, stop shutdown one process\r\n"
+ " --conf-file=[file] application config file, eg. application.xml、application.properties\r\n"
+ " restart restart one process\r\n"
+ " --conf-file=[file] application config file, eg. application.xml、application.properties\r\n"
+ " apidoc generate apidoc\r\n"
+ " --api-skiprpc=[true|false] skip @RestService(rpconly=true) service or @RestMapping(rpconly=true) method, default is true\r\n"
+ " --api-host=[url] api root url, default is http://localhost\r\n"
+ " help, -h, --help show this help\r\n";
}
NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) {
for (NodeServer node : servers) {
if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) {
return (NodeSncpServer) node;
}
}
return null;
}
public List<Object> command(String cmd, String[] params) {
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
List<Object> results = new ArrayList<>();
localServers.stream().forEach((server) -> {
try {
List<Object> rs = server.command(cmd, params);
if (rs != null) {
results.addAll(rs);
}
} catch (Exception t) {
logger.log(Level.WARNING, " command server(" + server.getSocketAddress() + ") error", t);
}
});
return results;
}
public void shutdown() throws Exception {
long f = System.currentTimeMillis();
for (ApplicationListener listener : this.listeners) {
try {
listener.preShutdown(this);
} catch (Exception e) {
logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e);
}
}
List<NodeServer> localServers = new ArrayList<>(servers); //顺序sncps, others, watchs
Collections.reverse(localServers); //倒序, 必须让watchs先关闭watch包含服务发现和注销逻辑
if (isCompileMode() && this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "MessageAgent stopping");
}
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.stop().join();
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms");
}
if (!isCompileMode() && clusterAgent != null) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "ClusterAgent destroying");
}
long s = System.currentTimeMillis();
clusterAgent.deregister(this);
clusterAgent.destroy(clusterAgent.getConfig());
logger.info("ClusterAgent destroy in " + (System.currentTimeMillis() - s) + " ms");
}
localServers.stream().forEach((server) -> {
try {
server.shutdown();
} catch (Exception t) {
logger.log(Level.WARNING, " shutdown server(" + server.getSocketAddress() + ") error", t);
} finally {
shutdownLatch.countDown();
}
});
if (this.messageAgents != null) {
Set<String> names = new HashSet<>();
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "MessageAgent destroying");
}
long s = System.currentTimeMillis();
for (MessageAgent agent : this.messageAgents) {
names.add(agent.getName());
agent.destroy(agent.getConfig());
}
logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") destroy in " + (System.currentTimeMillis() - s) + " ms");
}
for (DataSource source : dataSources) {
if (source == null) {
continue;
}
try {
if (source instanceof Service) {
long s = System.currentTimeMillis();
((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getResourceConf((Service) source) : null);
logger.info(source + " destroy in " + (System.currentTimeMillis() - s) + " ms");
// } else {
// source.getClass().getMethod("close").invoke(source);
// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close"));
}
} catch (Exception e) {
logger.log(Level.FINER, source.getClass() + " close DataSource erroneous", e);
}
}
for (CacheSource source : cacheSources) {
if (source == null) {
continue;
}
try {
if (source instanceof Service) {
long s = System.currentTimeMillis();
((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getResourceConf((Service) source) : null);
logger.info(source + " destroy in " + (System.currentTimeMillis() - s) + " ms");
// } else {
// source.getClass().getMethod("close").invoke(source);
// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close"));
}
} catch (Exception e) {
logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e);
}
}
if (this.propertiesAgent != null) {
long s = System.currentTimeMillis();
this.propertiesAgent.destroy(config.getAnyValue("properties"));
logger.info(this.propertiesAgent.getClass().getSimpleName() + " destroy in " + (System.currentTimeMillis() - s) + " ms");
}
if (this.clientAsyncGroup != null) {
long s = System.currentTimeMillis();
this.clientAsyncGroup.dispose();
logger.info("AsyncGroup destroy in " + (System.currentTimeMillis() - s) + " ms");
}
long intms = System.currentTimeMillis() - f;
String ms = String.valueOf(intms);
int repeat = ms.length() > 7 ? 0 : (7 - ms.length()) / 2;
logger.info(colorMessage(logger, 36, 1, "-".repeat(repeat) + "------------------------ Redkale shutdown in " + ms + " ms " + (ms.length() / 2 == 0 ? " " : "") + "-".repeat(repeat) + "------------------------") + "\r\n" + "\r\n");
LoggingBaseHandler.traceFlag = true;
}
public ExecutorService getWorkExecutor() {
return workExecutor;
}
public AsyncGroup getClientAsyncGroup() {
return clientAsyncGroup;
}
public ResourceFactory getResourceFactory() {
return resourceFactory;
}
public ClusterAgent getClusterAgent() {
return clusterAgent;
}
public MessageAgent getMessageAgent(String name) {
if (messageAgents == null) {
return null;
}
for (MessageAgent agent : messageAgents) {
if (agent.getName().equals(name)) {
return agent;
}
}
return null;
}
public MessageAgent[] getMessageAgents() {
return messageAgents;
}
public RedkaleClassLoader getClassLoader() {
return classLoader;
}
public RedkaleClassLoader getServerClassLoader() {
return serverClassLoader;
}
public List<NodeServer> getNodeServers() {
return new ArrayList<>(servers);
}
public List<DataSource> getDataSources() {
return new ArrayList<>(dataSources);
}
public List<CacheSource> getCacheSources() {
return new ArrayList<>(cacheSources);
}
public SncpRpcGroups getSncpRpcGroups() {
return sncpRpcGroups;
}
public int getNodeid() {
return nodeid;
}
public String getName() {
return name;
}
public File getHome() {
return home;
}
public URI getConfPath() {
return confPath;
}
public long getStartTime() {
return startTime;
}
public AnyValue getAppConfig() {
return config;
}
public boolean isCompileMode() {
return compileMode;
}
public boolean isSingletonMode() {
return singletonMode;
}
private static int parseLenth(String value, int defValue) {
if (value == null) {
return defValue;
}
value = value.toUpperCase().replace("B", "");
if (value.endsWith("G")) {
return Integer.decode(value.replace("G", "")) * 1024 * 1024 * 1024;
}
if (value.endsWith("M")) {
return Integer.decode(value.replace("M", "")) * 1024 * 1024;
}
if (value.endsWith("K")) {
return Integer.decode(value.replace("K", "")) * 1024;
}
return Integer.decode(value);
}
}