Merge remote-tracking branch 'origin/feature/1.4.0'

# Conflicts:
#	README.md
This commit is contained in:
aoshiguchen
2022-09-11 14:44:53 +08:00
38 changed files with 620 additions and 505 deletions
+28 -40
View File
@@ -1,7 +1,14 @@
# 1、简介
中微子代理(neutrino-proxy)是一个基于netty的、开源的java内网穿透项目。遵循MIT许可,因此您可以对它进行复制、修改、传播并用于任何个人或商业行为。
# 2、项目结构
# 2、运行示例
![用户管理](assets/user-manager1.png)
![端口池管理](assets/port-pool1.png)
![License管理](assets/license1.png)
![端口映射管理](assets/port-mapping1.png)
![客户端启动示例](assets/client-run1.png)
# 3、项目结构
- neutrino-proxy
- neutrino-core 与代理无关的基础封装
- neutrino-proxy-core 与代理相关的公共常量、编解码器
@@ -9,13 +16,13 @@
- neutrino-proxy-server 代理服务端项目
- neutrino-proxy-admin 代理监控项目(基于vue-element-admin开发)
# 3、运行
## 3.1、使用keytool工具生成ssl证书, 若不需要ssl加密可跳过
# 4、运行
## 4.1、使用keytool工具生成ssl证书, 若不需要ssl加密可跳过
```shell
keytool -genkey -alias test1 -keyalg RSA -keysize 1024 -validity 3650 -keypass 123456 -storepass 123456 -keystore "./test.jks"
```
## 3.2、修改服务端配置(application.yml
## 4.2、修改服务端配置(application.yml
```yml
application:
name: neutrino-proxy-server
@@ -39,18 +46,18 @@ proxy:
key-store-password: 123456
key-manager-password: 123456
# 证书存放路径,若不想打进jar包,可不带classpath:前缀
jks-path: classpath:/test.jks
# license配置, 客户端连接时需要用这个进行校验
license:
# license数为3表示用该license连接的客户端最多可代理3个端口,-1为不限
79419a1a8691413aa5e845b9e3e90051: 3
9352b1c25f564c81a5677131d7769876: 2
jks-path: classpath:/test.jks
data:
# 数据库配置(不用动,项目自动会自动初始化)
sqlite:
url: jdbc:sqlite:data.db
driver-class: org.sqlite.JDBC
```
## 3.3、启动服务端
## 4.3、启动服务端
> fun.asgc.neutrino.proxy.server.ProxyServer
## 3.4、修改客户端配置
## 4.4、修改客户端配置
```yml
application:
name: neutrino-proxy-client
@@ -76,35 +83,16 @@ proxy:
server-port: 9000
# 是否启用ssl,启用则必须配置ssl相关参数
ssl-enable: false
# 获取license提示间隔(秒)
obtain-license-interval: 5
```
## 3.5、准备代理信息配置文件 config.json
```
{
"environment": "我的Mac",
"clientKey": "79419a1a8691413aa5e845b9e3e90051", # 对应服务端配置license中的key
"proxy": [
{
"serverPort": 9100, # 外网服务器对外暴露的端口
"clientInfo": "127.0.0.1:3306" # 需要代理的本地端口(mysql)
},
{
"serverPort": 9101, # 外网服务器对外暴露的端口
"clientInfo": "rm-xxxx.mysql.rds.aliyuncs.com:3306" # 代理外网端口本身无意义,仅供测试
},
{
"serverPort": 9102, # 外网服务器对外暴露的端口
"clientInfo": "127.0.0.1:8080" # 需要代理的本地端口(http)
}
]
}
```
## 3.6、启动客户端
## 4.6、启动客户端
> fun.asgc.neutrino.proxy.client.ProxyClient
默认情况下,客户端会加载当前目录下的config.json文件作为代理配置,可通过命令行参数指定,如:java -jar neutrino-proxy-client.jar /xxx/proxy.json
默认情况下,客户端会加载当前目录下的.neutrino-proxy.license里的license,可通过命令行参数指定,如:java -jar neutrino-proxy-client.jar license=xxx
若启动参数未指定license,且是首次启动(当前目录下未缓存license),则需要根据命令行提示输入正确的license, 输入完成后完成连接,可在服务端管理页面控制端口转发,参见[2、运行示例](#2)
# 4、未来迭代方向
# 5、未来迭代方向
- 优化代码、增强稳定性
- 服务端增加管理页面,提供报表、授权、限流等功能
- 从项目中分离、孵化出另一个开源项目(neutrino-framework)
@@ -112,12 +100,12 @@ proxy:
# 5、技术文档
- [Aop](./docs/Aop.MD)
# 6、联系我们
# 7、联系我们
- 微信: yuyunshize
- Gitee(主更): https://gitee.com/asgc/neutrino-proxy
- Github: https://github.com/aoshiguchen/neutrino-proxy
# 7、特别鸣谢
* [JetBrains](https://www.jetbrains.com?from=neutrino-proxy)
# 8、特别鸣谢
* [JetBrains](https://www.jetbrains.com?from=RedisFront)
![JenBrains logo](assets/jetbrains.svg)
Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

@@ -24,10 +24,14 @@ package fun.asgc.neutrino.core.aop.compiler;
import com.google.common.collect.Lists;
import fun.asgc.neutrino.core.base.GlobalConfig;
import fun.asgc.neutrino.core.util.CollectionUtil;
import fun.asgc.neutrino.core.util.FileUtil;
import fun.asgc.neutrino.core.util.SystemUtil;
import lombok.extern.slf4j.Slf4j;
import javax.tools.*;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.stream.Collectors;
@@ -43,6 +47,7 @@ public class AsgcCompiler {
private final DiagnosticCollector<JavaFileObject> collector;
private final StandardJavaFileManager standardJavaFileManager;
private final List<String> options = new ArrayList<>();
private final List<String> defaultClassPathList = new ArrayList<>();
private final List<String> classpathList = new ArrayList<>();
private final Collection<JavaFileObject> compilationUnits = new ArrayList<JavaFileObject>();
private boolean isSaveSourceCodeFile;
@@ -65,7 +70,7 @@ public class AsgcCompiler {
this.standardJavaFileManager = javaCompiler.getStandardFileManager(collector, null, null);
this.isSaveClassFile = false;
this.generatorCodeSavePath = GlobalConfig.getGeneratorCodeSavePath();
this.dynamicClassLoader = new DynamicClassLoader(classLoader);
this.dynamicClassLoader = new DynamicClassLoader(classLoader, this);
addOption("-Xlint:unchecked");
addOption("-implicit:class");
@@ -74,6 +79,9 @@ public class AsgcCompiler {
}
public void addClasspath(String classpath) {
if (this.classpathList.contains(classpath)) {
return;
}
this.classpathList.add(classpath);
}
@@ -91,9 +99,10 @@ public class AsgcCompiler {
private List<String> getOptions() {
List<String> list = Lists.newArrayList(options);
if (!CollectionUtil.isEmpty(classpathList)) {
List<String> cp = getClasspathList();
if (!CollectionUtil.isEmpty(cp)) {
list.add("-classpath");
list.add(classpathList.stream().collect(Collectors.joining(File.pathSeparator)));
list.add(cp.stream().collect(Collectors.joining(File.pathSeparator)));
}
return list;
}
@@ -123,7 +132,15 @@ public class AsgcCompiler {
public Class<?> compile(String pkg, String className, String sourceCode) throws ClassNotFoundException {
log.info("options:" + getOptions());
JavaFileManager javaFileManager = new DynamicJavaFileManager(standardJavaFileManager, dynamicClassLoader);
Boolean result = javaCompiler.getTask(null, javaFileManager, collector, getOptions(), null, Lists.newArrayList(new StringSource(className, sourceCode))).call();
Iterable<? extends JavaFileObject> compilationUnits = Lists.newArrayList(new StringSource(className, sourceCode));
if (GlobalConfig.isSaveGeneratorCode()) {
javaFileManager = standardJavaFileManager;
File file = FileUtil.save(GlobalConfig.getGeneratorCodeSavePath() + pkg.replaceAll("\\.", "/"), className + ".java", sourceCode);
compilationUnits = standardJavaFileManager.getJavaFileObjects(file);
addClasspath(GlobalConfig.getGeneratorCodeSavePath());
}
Boolean result = javaCompiler.getTask(null, javaFileManager, collector, getOptions(), null, compilationUnits).call();
if (!result || collector.getDiagnostics().size() > 0) {
if (!result || collector.getDiagnostics().size() > 0) {
for (Diagnostic<? extends JavaFileObject> diagnostic : collector.getDiagnostics()) {
@@ -172,11 +189,61 @@ public class AsgcCompiler {
private void log() {
List<String> warnings = getWarnings();
List<String> errors = getErrors();
if (!CollectionUtil.isEmpty(warnings)) {
log.warn(warnings.stream().collect(Collectors.joining()));
}
// if (!CollectionUtil.isEmpty(warnings)) {
// log.warn(warnings.stream().collect(Collectors.joining()));
// }
if (!CollectionUtil.isEmpty(errors)) {
log.warn(errors.stream().collect(Collectors.joining()));
}
}
private URLClassLoader getURLClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
if (null == ret) {
ret = AsgcCompiler.class.getClassLoader();
}
return (ret instanceof URLClassLoader) ? (URLClassLoader)ret : null;
}
public List<String> getClasspathList() {
List<String> classpathList = new ArrayList<>();
List<String> defaultClasspathList = getDefaultClasspathList();
List<String> customClasspathList = this.classpathList;
if (!CollectionUtil.isEmpty(defaultClasspathList)) {
classpathList.addAll(defaultClasspathList);
}
if (!CollectionUtil.isEmpty(customClasspathList)) {
classpathList.addAll(customClasspathList);
}
return classpathList;
}
private synchronized List<String> getDefaultClasspathList() {
if (!CollectionUtil.isEmpty(defaultClassPathList)) {
return defaultClassPathList;
}
URLClassLoader classLoader = getURLClassLoader();
if (null == classLoader) {
return defaultClassPathList;
}
boolean isWindows = SystemUtil.isWindows();
for (URL url : classLoader.getURLs()) {
String path = url.getFile();
// 如果是 windows 系统,去除前缀字符 '/'
if (isWindows && path.startsWith("/")) {
path = path.substring(1);
}
// 去除后缀字符 '/'
if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
path = path.substring(0, path.length() - 1);
}
defaultClassPathList.add(path);
}
return defaultClassPathList;
}
}
@@ -29,7 +29,7 @@ import java.net.URI;
/**
*
* @author: wen.y
* @author: aoshiguchen
* @date: 2022/8/25
*/
public class CustomJavaFileObject implements JavaFileObject {
@@ -21,33 +21,158 @@
*/
package fun.asgc.neutrino.core.aop.compiler;
import java.util.HashMap;
import java.util.Map;
import fun.asgc.neutrino.core.util.ArrayUtil;
import fun.asgc.neutrino.core.util.CollectionUtil;
import fun.asgc.neutrino.core.util.FileUtil;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
*
* @author: wen.y
* @author: aoshiguchen
* @date: 2022/8/25
*/
public class DynamicClassLoader extends ClassLoader {
private final Map<String, MemoryByteCode> byteCodes = new HashMap<>();
private AsgcCompiler compiler;
private Map<String, Class<?>> classMap = new HashMap<>();
public DynamicClassLoader(ClassLoader classLoader) {
super(classLoader);
}
public DynamicClassLoader(ClassLoader classLoader, AsgcCompiler compiler) {
super(classLoader);
this.compiler = compiler;
}
public void registerCompiledSource(MemoryByteCode byteCode) {
byteCodes.put(byteCode.getClassName(), byteCode);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (classMap.containsKey(name)) {
return classMap.get(name);
}
MemoryByteCode byteCode = byteCodes.get(name);
if (null == byteCode) {
return super.findClass(name);
if (null != byteCode) {
return super.defineClass(name, byteCode.getByteCode(), 0, byteCode.getByteCode().length);
}
if (null != this.compiler) {
Class<?> ret = doFindClass(name, compiler.getClasspathList());
if (null != ret) {
return ret;
}
}
return super.findClass(name);
}
private Class<?> doFindClass(String name, List<String> classpathList) throws ClassNotFoundException {
if (CollectionUtil.isEmpty(classpathList)) {
return null;
}
String packageName = "";
if (name.lastIndexOf(".") != -1) {
packageName = name.substring(0, name.lastIndexOf("."));
}
for (String path : classpathList) {
try {
URL url = new URL("file:" + path);
if (path.endsWith(".jar")) {
url = new URL("jar:file:" + path + "!/");
}
Set<Class<?>> classSet = scan(packageName, url);
if (CollectionUtil.isEmpty(classSet)) {
continue;
}
Optional<Class<?>> classOptional = classSet.stream().filter(c -> c.getName().equals(name)).findFirst();
if (classOptional.isPresent()) {
return classOptional.get();
}
} catch (Exception e) {
// ignore
}
}
return null;
}
public Set<Class<?>> scan(String packageName, URL url) throws IOException, ClassNotFoundException, URISyntaxException {
Set<Class<?>> result = new HashSet<>();
if (null == url) {
return result;
}
String packagePath = packageName.replace(".", "/");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
int index = name.indexOf(packagePath);
if (index != -1 && name.endsWith(".class")) {
String replace = name.substring(index, name.length() - 6).replace("/", ".");
Class clazz = urlClassLoader.loadClass(replace);
result.add(clazz);
}
}
} else if ("file".endsWith(protocol)) {
String path = url.getPath();
String targetPath = path + "/" + packagePath;
addClasses(targetPath, result, packageName);
}
return result;
}
private synchronized void addClasses(String path, Set<Class<?>> classes, String packageName) throws ClassNotFoundException, URISyntaxException {
File[] files = new File(path).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
if (ArrayUtil.isEmpty(files)) {
return;
}
return super.defineClass(name, byteCode.getByteCode(), 0, byteCode.getByteCode().length);
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String className = fileName.substring(0, fileName.lastIndexOf("."));
String fullClassName = packageName + "." + className;
Class clazz = null;
try {
clazz = Thread.currentThread().getContextClassLoader().loadClass(fullClassName);
} catch (ClassNotFoundException e) {
if (this.classMap.containsKey(fullClassName)) {
clazz = this.classMap.get(fileName);
} else {
byte[] byteCode = FileUtil.readBytes(file);
clazz = super.defineClass(fullClassName, byteCode, 0, byteCode.length);
this.classMap.put(fullClassName, clazz);
}
}
if (null != clazz) {
classes.add(clazz);
}
} else {
String subPackagePath = path + "/" + fileName;
String subPackageName = packageName + "." + fileName;
addClasses(subPackagePath, classes, subPackageName);
}
}
}
public Map<String, Class<?>> getClasses() throws ClassNotFoundException {
@@ -1,31 +0,0 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.aop.compiler;
/**
*
* @author: aoshiguchen
* @date: 2022/8/26
*/
public @interface DynamicCompile {
}
@@ -1,50 +0,0 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.aop.compiler;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
/**
*
* @author: aoshiguchen
* @date: 2022/8/26
*/
@SupportedAnnotationTypes("DynamicCompile")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DynamicCompileProcess extends AbstractProcessor {
// @Override
// public synchronized void init(ProcessingEnvironment processingEnv) {
// super.init(processingEnv);
// }
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, "---------Hello World!");
return true;
}
}
@@ -39,6 +39,7 @@ public class MemoryByteCode extends SimpleJavaFileObject {
private static final String CLASS_FILE_SUFFIX = ".class";
private ByteArrayOutputStream byteArrayOutputStream;
private byte[] byteCode;
public MemoryByteCode(String className) {
super(URI.create("byte:///" + className.replace(PKG_SEPARATOR, DIR_SEPARATOR)
@@ -51,6 +52,12 @@ public class MemoryByteCode extends SimpleJavaFileObject {
this.byteArrayOutputStream = byteArrayOutputStream;
}
public MemoryByteCode(String className, byte[] byteCode)
throws URISyntaxException {
this(className);
this.byteCode = byteCode;
}
@Override
public OutputStream openOutputStream() throws IOException {
if (byteArrayOutputStream == null) {
@@ -60,7 +67,7 @@ public class MemoryByteCode extends SimpleJavaFileObject {
}
public byte[] getByteCode() {
return byteArrayOutputStream.toByteArray();
return null == byteCode ? byteArrayOutputStream.toByteArray() : byteCode;
}
public String getClassName() {
@@ -21,8 +21,6 @@
*/
package fun.asgc.neutrino.core.aop.compiler;
import fun.asgc.neutrino.core.util.CollectionUtil;
import javax.tools.JavaFileObject;
import java.io.File;
import java.io.IOException;
@@ -21,6 +21,7 @@
*/
package fun.asgc.neutrino.core.aop.proxy;
import fun.asgc.neutrino.core.aop.compiler.AsgcCompiler;
import fun.asgc.neutrino.core.base.GlobalConfig;
import fun.asgc.neutrino.core.util.*;
import lombok.extern.slf4j.Slf4j;
@@ -38,7 +39,7 @@ public class AsgcProxyFactory implements ProxyFactory {
private static final String SYMBOLIC = "AsgcProxy$$";
private static final String classNameTemplate = "%s" + SYMBOLIC + "%s";
private static AtomicLong proxyClassCounter = new AtomicLong();
private ProxyCompiler compiler = new ProxyCompiler();
private AsgcCompiler compiler = new AsgcCompiler();
private ProxyClassLoader classLoader = new ProxyClassLoader();
@Override
@@ -85,20 +86,11 @@ public class AsgcProxyFactory implements ProxyFactory {
if (GlobalConfig.isPrintGeneratorCode()) {
log.debug("类:{} 的代理类源码:\n{}", targetType.getName(), sourceCode);
}
Class<P> retClass = compile(proxyClass);
Class<P> retClass = (Class<P>)compiler.compile(proxyClass.getPkg(), proxyClass.getName(), proxyClass.getSourceCode());
P obj = retClass.newInstance();
return obj;
}
private <T> Class<T> compile(ProxyClass proxyClass) throws ClassNotFoundException {
if (SystemUtil.isStartupFromJar() || GlobalConfig.isSaveGeneratorCode()) {
compiler.compileToFile(proxyClass);
return (Class<T>)classLoader.loadProxyClass(proxyClass);
}
compiler.compile(proxyClass);
return (Class<T>)classLoader.loadProxyClass(proxyClass);
}
private String generateClassName(Class<?> clazz) {
return String.format(classNameTemplate, clazz.getSimpleName(), proxyClassCounter.incrementAndGet());
}
@@ -1,294 +0,0 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.aop.proxy;
import fun.asgc.neutrino.core.base.GlobalConfig;
import fun.asgc.neutrino.core.util.FileUtil;
import fun.asgc.neutrino.core.util.SystemUtil;
import lombok.extern.slf4j.Slf4j;
import javax.tools.*;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.stream.Collectors;
/**
*
* @author: aoshiguchen
* @date: 2022/6/24
*/
@Slf4j
public class ProxyCompiler {
/**
* 收集编译过程信息
*/
private static DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
protected volatile List<String> options = null;
protected volatile boolean isUnpack = false;
protected List<String> getOptions() {
if (options != null) {
return options;
}
synchronized (this) {
if (options != null) {
return options;
}
List<String> ret = new ArrayList<>();
ret.add("-target");
ret.add("1.8");
String cp = getClassPath();
if (cp != null && cp.trim().length() != 0) {
ret.add("-classpath");
ret.add(cp);
}
options = ret;
return options;
}
}
/**
* 兼容 tomcat 丢失 class path,否则无法编译
*/
protected String getClassPath() {
if (!SystemUtil.isStartupFromJar()) {
URLClassLoader classLoader = getURLClassLoader();
if (classLoader == null) {
return null;
}
int index = 0;
boolean isWindows = SystemUtil.isWindows();
StringBuilder ret = new StringBuilder();
for (URL url : classLoader.getURLs()) {
if (index++ > 0) {
ret.append(File.pathSeparator);
}
String path = url.getFile();
// 如果是 windows 系统,去除前缀字符 '/'
if (isWindows && path.startsWith("/")) {
path = path.substring(1);
}
// 去除后缀字符 '/'
if (path.length() > 1 && (path.endsWith("/") || path.endsWith(File.separator))) {
path = path.substring(0, path.length() - 1);
}
ret.append(path);
}
return ret.toString();
}
List<String> list = new ArrayList<>();
File file = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/lib/");
if (file.exists()) {
File[] files = file.listFiles();
for (File f : files) {
list.add(f.getAbsolutePath());
}
}
list.add(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/classes/");
return list.stream().collect(Collectors.joining(File.pathSeparator));
}
protected URLClassLoader getURLClassLoader() {
ClassLoader ret = Thread.currentThread().getContextClassLoader();
if (ret == null) {
ret = ProxyCompiler.class.getClassLoader();
}
return (ret instanceof URLClassLoader) ? (URLClassLoader)ret : null;
}
public void compile(ProxyClass proxyClass) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \n" +
"Visit https://jfinal.com/doc/4-8 for details \n");
}
DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
try (MyJavaFileManager javaFileManager = new MyJavaFileManager(compiler.getStandardFileManager(collector, null, null))) {
MyJavaFileObject javaFileObject = new MyJavaFileObject(proxyClass.getName(), proxyClass.getSourceCode());
Boolean result = compiler.getTask(null, javaFileManager, collector, getOptions(), null, Arrays.asList(javaFileObject)).call();
outputCompileError(result, collector);
Map<String, byte[]> ret = new HashMap<>();
for (Map.Entry<String, MyJavaFileObject> e : javaFileManager.fileObjects.entrySet()) {
ret.put(e.getKey(), e.getValue().getByteCode());
}
proxyClass.setByteCode(ret);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void compileToFile(ProxyClass proxyClass) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new RuntimeException("Can not get javax.tools.JavaCompiler, check whether \"tools.jar\" is in the environment variable CLASSPATH \n" +
"Visit https://jfinal.com/doc/4-8 for details \n");
}
unpack();
File file = FileUtil.save(GlobalConfig.getGeneratorCodeSavePath() + proxyClass.getPkg().replaceAll("\\.", "/"), proxyClass.getName() + ".java", proxyClass.getSourceCode());
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
// 创建一个编译任务
JavaCompiler.CompilationTask task = compiler.getTask(null, standardFileManager, diagnostics, getOptions(), null, iterable);
//JavaCompiler.CompilationTask 实现了 Callable 接口
Boolean result = task.call();
printLog(result, file);
}
private synchronized void unpack() {
if (!SystemUtil.isStartupFromJar()) {
return;
}
if (isUnpack) {
return;
}
isUnpack = true;
try {
File file1 = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/lib/");
File file2 = new File(GlobalConfig.getGeneratorCodeSavePath() + "BOOT-INF/classes/");
if (file1.exists() && file2.exists()) {
return;
}
log.info("解压jar:{} 到:{}", SystemUtil.getCurrentJarFilePath(), GlobalConfig.getGeneratorCodeSavePath());
FileUtil.unzipJar(GlobalConfig.getGeneratorCodeSavePath(), SystemUtil.getCurrentJarFilePath());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void printLog(Boolean result, File ...files){
if (!result) {
StringJoiner rs = new StringJoiner(System.getProperty("line.separator"));
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
rs.add(String.format("%s:%s[line %d column %d]-->%s%n", diagnostic.getKind(), diagnostic.getSource(), diagnostic.getLineNumber(),
diagnostic.getColumnNumber(),
diagnostic.getMessage(null)));
}
log.error("编译失败,原因:{}", rs.toString());
} else {
StringBuilder sb = new StringBuilder();
Arrays.stream(files).forEach(file -> {
sb.append(file.getName());
sb.append(";");
});
}
}
protected void outputCompileError(Boolean result, DiagnosticCollector<JavaFileObject> collector) {
if (! result) {
collector.getDiagnostics().forEach(item -> log.error(item.toString()));
}
}
public ProxyCompiler setCompileOptions(List<String> options) {
Objects.requireNonNull(options, "options can not be null");
this.options = options;
return this;
}
public ProxyCompiler addCompileOption(String option) {
Objects.requireNonNull(option, "option can not be null");
options.add(option);
return this;
}
public static class MyJavaFileObject extends SimpleJavaFileObject {
private String source;
private ByteArrayOutputStream outPutStream;
public MyJavaFileObject(String name, String source) {
super(URI.create("String:///" + name + JavaFileObject.Kind.SOURCE.extension), JavaFileObject.Kind.SOURCE);
this.source = source;
}
public MyJavaFileObject(String name, JavaFileObject.Kind kind) {
super(URI.create("String:///" + name + kind.extension), kind);
source = null;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
if (source == null) {
throw new IllegalStateException("source field can not be null");
}
return source;
}
@Override
public OutputStream openOutputStream() throws IOException {
outPutStream = new ByteArrayOutputStream();
return outPutStream;
}
public byte[] getByteCode() {
return outPutStream.toByteArray();
}
}
public static class MyJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
public Map<String, MyJavaFileObject> fileObjects = new HashMap<>();
public MyJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
MyJavaFileObject javaFileObject = new MyJavaFileObject(qualifiedClassName, kind);
fileObjects.put(qualifiedClassName, javaFileObject);
return javaFileObject;
}
// 是否在编译时依赖另一个类的情况下用到本方法 ?
@Override
public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException {
JavaFileObject javaFileObject = fileObjects.get(className);
if (javaFileObject == null) {
javaFileObject = super.getJavaFileForInput(location, className, kind);
}
return javaFileObject;
}
}
}
@@ -128,39 +128,50 @@ public class ClassUtil {
}
log.info("开始进行类扫描,扫描包:{}", packageName);
Set<Class<?>> classes = new HashSet<>();
String packagePath = packageName.replace(".", "/");
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("");
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
log.info(url.toString());
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
int index = name.indexOf(packagePath);
if (index != -1 && name.endsWith(".class")) {
String replace = name.substring(index, name.length() - 6).replace("/", ".");
Class clazz = urlClassLoader.loadClass(replace);
log.debug("scan class {}", clazz.getName());
classes.add(clazz);
}
}
} else if ("file".endsWith(protocol)) {
String path = url.getPath();
String targetPath = path + "/" + packagePath;
addClasses(targetPath, classes, packageName);
}
classes.addAll(scan(packageName, resources.nextElement()));
}
if (SystemUtil.isStartupFromJar()) {
classes.addAll(scan(packageName, new URL("jar:file:" + SystemUtil.getCurrentJarFilePath() + "!/")));
}
log.info("扫描完毕,包:{}下一共有:{}个类", packageName, classes.size());
classesCache.set(packageName, classes);
return classes;
}
public static Set<Class<?>> scan(String packageName, URL url) throws IOException, ClassNotFoundException {
Set<Class<?>> result = new HashSet<>();
if (null == url) {
return result;
}
String packagePath = packageName.replace(".", "/");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
log.info(url.toString());
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
int index = name.indexOf(packagePath);
if (index != -1 && name.endsWith(".class")) {
String replace = name.substring(index, name.length() - 6).replace("/", ".");
Class clazz = urlClassLoader.loadClass(replace);
log.debug("scan class {}", clazz.getName());
result.add(clazz);
}
}
} else if ("file".endsWith(protocol)) {
String path = url.getPath();
String targetPath = path + "/" + packagePath;
addClasses(targetPath, result, packageName);
}
return result;
}
private static void addClasses(String path, Set<Class<?>> classes, String packageName) throws ClassNotFoundException {
File[] files = new File(path).listFiles(new FileFilter() {
@Override
@@ -111,18 +111,30 @@ public class FileUtil {
*/
public static byte[] readBytes(String path) {
try (InputStream in = getInputStream(path)){
byte[] bytes = new byte[1024];
int length = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((length = in.read(bytes)) != -1) {
baos.write(bytes, 0, length);
}
return baos.toByteArray();
return readBytes(in);
} catch (Exception e) {
return null;
}
}
public static byte[] readBytes(File file) {
try (InputStream in = new FileInputStream(file)){
return readBytes(in);
} catch (Exception e) {
return null;
}
}
public static byte[] readBytes(InputStream in) throws IOException {
byte[] bytes = new byte[1024];
int length = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((length = in.read(bytes)) != -1) {
baos.write(bytes, 0, length);
}
return baos.toByteArray();
}
public static void write(String path, String content) {
// try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(path), StandardCharsets.UTF_8)){
// writer.write(content);
@@ -88,15 +88,16 @@ public class AsgcCompilerTest {
compiler.addClasspath("/Users/yangwen/my/tmp/java");
compiler.addClasspath("/Users/yangwen/my/tmp/java/asgc-package-lab1-1.0-SNAPSHOT.jar");
String code = "package a.b;\n" +
"import fun.asgc.lab.pkg.lab1.Dog1;\n" +
"import fun.asgc.lab.pkg.lab1.Dog;\n" +
"import fun.asgc.cptest.Player;\n" +
"public class RadioPlayer implements Player {\n" +
"\tpublic void play() {\n" +
// "\t\tSystem.out.println(Dog.class);\n" +
// "\t\tSystem.out.println(new Dog(\"大黄\").eat(\"骨头\"));\n" +
"\t\tSystem.out.println(Dog.class);\n" +
"\t\tSystem.out.println(new Dog(\"大黄\").eat(\"骨头\"));\n" +
"\t\tSystem.out.println(\"收音机播放\");\n" +
"\t}\n" +
"}\n";
// GlobalConfig.setIsSaveGeneratorCode(true);
Class clazz = compiler.compile("a.b","RadioPlayer", code);
Method method = ReflectUtil.getMethods(clazz).stream().filter(m -> m.getName().equals("play")).findFirst().get();
Object instance = clazz.newInstance();
@@ -25,6 +25,8 @@ import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -71,4 +73,12 @@ public class ClassUtilTest {
}
}
@Test
public void scan() throws Exception {
String path = "jar:file:/Users/yangwen/my/tmp/java/neutrino-proxy-server-1.0-SNAPSHOT-jar-with-dependencies.jar!/";
URL url = new URL(path);
Set<Class<?>> c = ClassUtil.scan("fun.asgc.neutrino.proxy.server", url);
System.out.println(c);
}
}
@@ -0,0 +1,228 @@
<template>
<div class="drop-down-table">
<div class="input-tag-box">
<div id="tag-list-box">
<el-tag class="elTag" v-show="tags.length > 0" v-for="(tag,index) in tags" closable type="info" size="mini" @close="handeCloseTag(index,tag)">{{tag.name}}</el-tag>
<el-input v-model="inputValue"
:placeholder="tags.length > 0 ? '':'请选择'"
@focus="editor = true"
@keyup.backspace.native="keyup"
:style="{width:inputWidth}"/>
</div>
<i :class="editor ? iconUp : iconDown" @click="editor = !editor" style="color: #C0C4CC;"/>
</div>
<div class="popup-class" :style="{'width':width}" v-show="editor">
<el-table
border
:columns="columns"
:data="getData"
@row-click="handeRowClick"
max-height="500"
:row-class-name="tableRowClassName"
header-cell-class-name="table_header_class"
>
<el-table-column v-for="item in columns" align="center" :prop="item.key" :label="item.title" :min-width="item.minWidth"/>
</el-table>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: ''
},
height: {
type: String,
default: ''
},
width: {
type: String,
default: '400px'
},
data: {
type: Array,
default: []
// required: true
},
columns: {
type: Array,
default: []
// required: true
},
multiple: {
type: Boolean,
default: false
}
},
computed: {
getData() {
if (!this.multiple) {
return this.data
} else {
return this.data.filter(item => !this.inputValue || item.name.toLowerCase().includes(this.inputValue.toLowerCase()))
}
}
},
data() {
return {
editor: false,
inputValue: '',
iconUp: 'el-select__caret el-input__icon el-icon-arrow-up',
iconDown: 'el-select__caret el-input__icon el-icon-arrow-down',
tags: [],
inputWidth: '100%'
}
},
created() {
this.inputValue = this.value
},
watch: {
editor() {
if (this.editor) {
document.getElementsByClassName('input-tag-box')[0].style.borderColor = '#409EFF'
} else {
this.inputValue = ''
document.getElementsByClassName('input-tag-box')[0].style.borderColor = '#909399'
}
}
},
mounted() {
},
methods: {
tableRowClassName({ row, rowIndex }) {
row.index = rowIndex
let className = 'success-row'
if (row.disabled) {
className = 'disabled-row'
}
const _index = this.tags.findIndex(item => item === row)
if (_index > -1) {
return className + ' select-row'
}
return className
},
handeRowClick(row, column, event) {
if (row.disabled) {
return
}
if (this.multiple) {
const _index = this.tags.findIndex(item => item === row)
if (_index > -1) {
this.tags.splice(_index, 1)
} else {
this.tags.push(row)
}
setTimeout(() => {
this.getInputWidth()
})
} else {
this.editor = false
this.inputValue = row.name
this.tags = [row]
}
this.$emit('rowClick', row, this.tags, column, event)
},
handeCloseTag(index, tag) {
this.tags.splice(index, 1)
this.inputValue = ''
},
keyup() {
if (this.inputValue.length <= 0 && this.tags.length > 0) {
const list = document.getElementsByClassName('elTag')
if (list[list.length - 1].style.borderColor !== '') {
this.handeCloseTag(this.tags.length - 1)
} else {
list[list.length - 1].style.borderColor = '#909399'
}
}
},
getInputWidth() {
let tagMaxWidth = 0
const offsetWidth = document.getElementById('tag-list-box').offsetWidth
const elTagName = document.getElementsByClassName('elTag')
elTagName.forEach(item => {
const width = item.offsetWidth + 5.5
if (width + tagMaxWidth < offsetWidth) {
tagMaxWidth = width + tagMaxWidth
} else {
tagMaxWidth = width
}
})
this.inputWidth = (offsetWidth - tagMaxWidth) + 'px'
}
}
}
</script>
<style lang="less" scoped>
.drop-down-table{
.input-tag-box{
display: flex;
background: #fff;
align-items: center;
border-radius: 4px;
border: 1px solid #DCDFE6;
#tag-list-box{
padding: 0;
width: ~"calc(100% - 30px)";;
.elTag{
margin-left: 5px;
margin-top: 5px;
}
}
/deep/.el-input{
display: inline-block;
}
/deep/.el-input__inner{
border: 0px!important;
padding-right: 0;
}
/deep/.el-input__icon{
line-height: 34px!important;
}
}
.popup-class{
padding: 8px;
margin-top: 5px;
width: 100%;
background: white;
border: 1px solid #f0f2f5;
box-shadow: 1px 1px 10px #f0f2f5;
position: absolute;
z-index: 9;
/deep/.ivu-table-tip{
overflow-x: hidden!important;
}
}
/deep/.table_header_class{
font-weight: bolder;
color: black;
background-color: #ebeef5!important;
}
/deep/.gutter{
background-color: #ebeef5!important;
}
/deep/.el-table .success-row {
&:hover{
cursor: pointer!important;
}
}
/deep/.el-table .disabled-row {
color: #c0c4cc !important;
&:hover{
cursor: not-allowed!important;
}
&:hover>td{
background-color: #FFF!important;
}
}
/deep/.el-table .select-row {
color: #409eff;
font-weight: 700;
}
}
</style>
@@ -89,7 +89,16 @@
:width="280"
:disabled="dialogStatus==='update'"
/>
<!-- <DropdownTable
:columns="countryColumns"
:data="licenseList"
v-model="temp.licenseId"
:value="temp.licenseName"
@rowClick="selectedFeeItem"
disabled
:disabled="dialogStatus==='update'"
style="width: 280px"
/>-->
</el-form-item>
<el-form-item :label="$t('服务端端口')" prop="serverPort">
<el-select style="width: 280px;" class="filter-item" v-model="temp.serverPort" placeholder="请选择">
@@ -197,7 +206,11 @@
clientIp: [{ required: true, message: '请输入客户端IP', trigger: 'blur' }],
clientPort: [{ required: true, message: '请输入客户端端口', trigger: 'blur' }]
},
downloadLoading: false
downloadLoading: false,
countryColumns: [
{ prop: 'userName', label: '用户名', align: 'center' },
{ prop: 'name', label: 'License', align: 'center' }
]
}
},
filters: {
@@ -342,10 +355,9 @@
}
})
},
selectedFeeItem(list) {
console.log(list)
this.temp.licenseId = list.id
this.temp.licenseName = list.name
selectedFeeItem(row, list) {
this.temp.licenseId = row.id
this.temp.licenseName = row.name
},
handleDelete(row) {
this.$confirm('确定要删除吗?', '提示', {
@@ -98,6 +98,7 @@ public class LicenseObtainService implements ApplicationRunner {
for (String s : args) {
if (s.startsWith("license=") && s.length() > 8) {
license = s.substring(8).trim();
break;
}
}
}
+36 -6
View File
@@ -32,20 +32,50 @@
</dependency>
</dependencies>
<!-- <build>-->
<!-- <finalName>${artifactId}</finalName>-->
<!-- <plugins>-->
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- <version>2.1.3.RELEASE</version>-->
<!-- <configuration>-->
<!-- <mainClass>fun.asgc.neutrino.proxy.server.ProxyServer</mainClass>-->
<!-- </configuration>-->
<!-- <executions>-->
<!-- <execution>-->
<!-- <goals>-->
<!-- <goal>repackage</goal>-->
<!-- </goals>-->
<!-- </execution>-->
<!-- </executions>-->
<!-- </plugin>-->
<!-- </plugins>-->
<!-- </build>-->
<build>
<finalName>${artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.3.RELEASE</version>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<mainClass>fun.asgc.neutrino.proxy.server.ProxyServer</mainClass>
<appendAssemblyId>false</appendAssemblyId>
<finalName>${artifactId}</finalName>
<archive>
<manifest>
<!--这里指定要运行的main类-->
<mainClass>fun.asgc.neutrino.proxy.server.ProxyServer</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>repackage</goal>
<goal>single</goal>
</goals>
</execution>
</executions>
@@ -24,6 +24,7 @@ package fun.asgc.neutrino.proxy.server;
import fun.asgc.neutrino.core.annotation.EnableJob;
import fun.asgc.neutrino.core.annotation.NeutrinoApplication;
import fun.asgc.neutrino.core.base.GlobalConfig;
import fun.asgc.neutrino.core.context.NeutrinoLauncher;
/**
@@ -36,6 +37,7 @@ import fun.asgc.neutrino.core.context.NeutrinoLauncher;
public class ProxyServer {
public static void main(String[] args) {
GlobalConfig.setIsSaveGeneratorCode(true);
NeutrinoLauncher.run(ProxyServer.class, args).sync();
}
@@ -29,14 +29,20 @@ import lombok.Data;
import java.util.Map;
/**
*
* 服务端代理配置
* @author: aoshiguchen
* @date: 2022/6/16
*/
@Data
@Configuration(prefix = "neutrino.proxy")
public class ProxyConfig {
/**
* 传输协议相关配置
*/
private Protocol protocol;
/**
* 服务端配置
*/
private Server server;
@Data
@@ -27,7 +27,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* 鉴权注解
* @author: aoshiguchen
* @date: 2022/7/31
*/
@@ -33,14 +33,14 @@ import java.sql.DriverManager;
import java.util.List;
/**
*
* @author: 初始化数据库
* 初始化数据库
* @author: aoshiguchen
* @date: 2022/7/31
*/
@Slf4j
@PreLoad("init")
public class DBInitialize {
private static List<String> initDataTableNameList = Lists.newArrayList("user", "license", "port_pool", "port_mapping", "job_qrtz_trigger_info");
private static List<String> initDataTableNameList = Lists.newArrayList("user", "license", "port_pool", "port_mapping", "job_info");
private static SqliteConfig sqliteConfig;
private static JdbcTemplate jdbcTemplate;
@@ -25,7 +25,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
/**
*
* 响应体
* @author: aoshiguchen
* @date: 2022/7/31
*/
@@ -26,7 +26,7 @@ import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
/**
*
* 服务异常
* @author: aoshiguchen
* @date: 2022/7/31
*/
@@ -28,7 +28,7 @@ import lombok.experimental.Accessors;
import java.util.Date;
/**
*
* 系统上下文
* @author: aoshiguchen
* @date: 2022/8/2
*/
@@ -24,7 +24,7 @@ package fun.asgc.neutrino.proxy.server.base.rest;
import fun.asgc.neutrino.proxy.server.dal.entity.UserDO;
/**
*
* 系统上下文持有者
* @author: aoshiguchen
* @date: 2022/8/2
*/
@@ -34,7 +34,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
*
* 定时任务配置
* @author: aoshiguchen
* @date: 2022/9/4
*/
@@ -28,7 +28,7 @@ import fun.asgc.neutrino.core.annotation.Value;
import lombok.Data;
/**
*
* sqlite数据库配置
* @author: aoshiguchen
* @date: 2022/7/31
*/
@@ -29,7 +29,7 @@ import fun.asgc.neutrino.core.web.interceptor.RestControllerAdviceHandler;
import fun.asgc.neutrino.proxy.server.base.rest.interceptor.*;
/**
*
* web配置
* @author: aoshiguchen
* @date: 2022/7/30
*/
@@ -30,7 +30,7 @@ import fun.asgc.neutrino.proxy.server.base.rest.ServiceException;
import org.apache.commons.lang3.exception.ExceptionUtils;
/**
*
* 全局异常处理
* @author: aoshiguchen
* @date: 2022/7/31
*/
@@ -25,7 +25,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
* 连接类型枚举
* @author: aoshiguchen
* @date: 2022/8/31
*/
@@ -25,7 +25,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
* 异常常量枚举
* @author: aoshiguchen
* @date: 2022/7/31
*/