diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/AsgcCompiler.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/AsgcCompiler.java index 3b520ddb..4d5edf18 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/AsgcCompiler.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/AsgcCompiler.java @@ -24,6 +24,7 @@ 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; @@ -78,6 +79,9 @@ public class AsgcCompiler { } public void addClasspath(String classpath) { + if (this.classpathList.contains(classpath)) { + return; + } this.classpathList.add(classpath); } @@ -128,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 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 diagnostic : collector.getDiagnostics()) { diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/DynamicClassLoader.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/DynamicClassLoader.java index 0c1fe797..be7251c3 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/DynamicClassLoader.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/DynamicClassLoader.java @@ -21,11 +21,22 @@ */ package fun.asgc.neutrino.core.aop.compiler; +import fun.asgc.neutrino.core.base.GlobalConfig; +import fun.asgc.neutrino.core.util.ArrayUtil; import fun.asgc.neutrino.core.util.ClassUtil; 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; /** * @@ -35,6 +46,7 @@ import java.util.*; public class DynamicClassLoader extends ClassLoader { private final Map byteCodes = new HashMap<>(); private AsgcCompiler compiler; + private Map> classMap = new HashMap<>(); public DynamicClassLoader(ClassLoader classLoader) { super(classLoader); @@ -51,6 +63,9 @@ public class DynamicClassLoader extends ClassLoader { @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.defineClass(name, byteCode.getByteCode(), 0, byteCode.getByteCode().length); @@ -78,7 +93,7 @@ public class DynamicClassLoader extends ClassLoader { if (path.endsWith(".jar")) { url = new URL("jar:file:" + path + "!/"); } - Set> classSet = ClassUtil.scan(packageName, url); + Set> classSet = scan(packageName, url); if (CollectionUtil.isEmpty(classSet)) { continue; } @@ -93,6 +108,69 @@ public class DynamicClassLoader extends ClassLoader { return null; } + public Set> scan(String packageName, URL url) throws IOException, ClassNotFoundException, URISyntaxException { + Set> 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 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> 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; + } + + 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; + if (path.startsWith(GlobalConfig.getGeneratorCodeSavePath())) { + byte[] byteCode = FileUtil.readBytes(file); + clazz = super.defineClass(fullClassName, byteCode, 0, byteCode.length); + this.classMap.put(fullClassName, clazz); + } else { + clazz = Thread.currentThread().getContextClassLoader().loadClass(fullClassName); + } + classes.add(clazz); + } else { + String subPackagePath = path + "/" + fileName; + String subPackageName = packageName + "." + fileName; + addClasses(subPackagePath, classes, subPackageName); + } + } + } + public Map> getClasses() throws ClassNotFoundException { Map> classes = new HashMap<>(); for (MemoryByteCode byteCode : byteCodes.values()) { diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/MemoryByteCode.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/MemoryByteCode.java index 07428ff1..f48f9d05 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/MemoryByteCode.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/aop/compiler/MemoryByteCode.java @@ -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() { diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java index f221ef07..c2ab8954 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/util/FileUtil.java @@ -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); diff --git a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java index 08b98a69..437b04f8 100644 --- a/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java +++ b/neutrino-proxy-server/src/main/java/fun/asgc/neutrino/proxy/server/ProxyServer.java @@ -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(); }