boot jar 동적컴파일 대응
This commit is contained in:
@@ -22,6 +22,7 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||
// implementation 'com.github.darrachequesne:spring-data-jpa-datatables:5.1.0'
|
||||
// implementation 'com.google.jimfs:jimfs:1.2'
|
||||
|
||||
implementation 'org.apache.commons:commons-lang3:3.10'
|
||||
implementation 'commons-io:commons-io:2.6'
|
||||
|
||||
@@ -2,12 +2,16 @@ package com.eactive.httpmockserver;
|
||||
|
||||
import com.eactive.httpmockserver.script.ScriptInfoService;
|
||||
import com.eactive.httpmockserver.script.ScriptLoader;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Component
|
||||
public class PostConstructBean {
|
||||
@@ -17,7 +21,7 @@ public class PostConstructBean {
|
||||
public ScriptInfoService scriptInfoService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
public void init() throws URISyntaxException {
|
||||
logger.info("init scripts");
|
||||
try {
|
||||
ScriptLoader.getInstance().init(scriptInfoService);
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
package com.eactive.httpmockserver.script;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mdkt.compiler.CompilationException;
|
||||
import org.mdkt.compiler.CompiledCode;
|
||||
import org.mdkt.compiler.DynamicClassLoader;
|
||||
import org.mdkt.compiler.InMemoryJavaCompiler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.ToolProvider;
|
||||
import javax.tools.*;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public class ScriptLoader{
|
||||
String path = ScriptLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||
@@ -24,9 +24,63 @@ public class ScriptLoader{
|
||||
|
||||
private static final ScriptLoader instance = new ScriptLoader();
|
||||
private Map<String, Class<Script>> loadedClass;
|
||||
private String classPath = null;
|
||||
|
||||
/**
|
||||
* BOOT 일 경우 JAR 내의 Class 목록이 BOOT-INF로 들어가서 컴파일시 참조가 안되는 문제가 있음
|
||||
* 임시로 /tmp 폴더에 압축해제하여 해당 경로를 참조하게 하였으나 메모리 내에서 참조가능거나 jar파일을 직접 참조 할 수 있는 방안을 찾아볼 예정임
|
||||
* Groovy 스크립트로 변경도 검토 중
|
||||
*/
|
||||
private ScriptLoader(){
|
||||
this.loadedClass = new HashMap<>();
|
||||
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
|
||||
try {
|
||||
String uri = url.toURI().toString();
|
||||
System.out.println(uri);
|
||||
if(StringUtils.contains(uri, "jar:file:" )) {
|
||||
String jarPath = StringUtils.substringBetween(uri, "jar:file:/", ".jar!") + ".jar";
|
||||
System.out.println("jarPath => " + jarPath);
|
||||
|
||||
if(StringUtils.contains(uri, "BOOT-INF" )) {
|
||||
String memoryWorkingPath = "/tmp/httpmockworking";
|
||||
File workingDir = new File(memoryWorkingPath);
|
||||
workingDir.mkdirs();
|
||||
|
||||
java.util.jar.JarFile jar = new java.util.jar.JarFile(jarPath);
|
||||
java.util.Enumeration enumEntries = jar.entries();
|
||||
while (enumEntries.hasMoreElements()) {
|
||||
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement();
|
||||
java.io.File f = new java.io.File(memoryWorkingPath + "/" + file.getName());
|
||||
if (file.isDirectory()) { // if its a directory, create it
|
||||
f.mkdirs();
|
||||
continue;
|
||||
}
|
||||
java.io.InputStream is = jar.getInputStream(file); // get the input stream
|
||||
BufferedInputStream bis = new BufferedInputStream(is);
|
||||
|
||||
java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(fos);
|
||||
|
||||
int numBytes;
|
||||
byte[] buffer = new byte[1024];
|
||||
while ((numBytes = bis.read(buffer))!= -1)
|
||||
{
|
||||
bos.write(buffer, 0, numBytes);
|
||||
}
|
||||
bis.close();
|
||||
bos.close();
|
||||
}
|
||||
jar.close();
|
||||
classPath = memoryWorkingPath+"/BOOT-INF/classes;"+memoryWorkingPath+"/BOOT-INF/lib/*;";
|
||||
}else {
|
||||
classPath = jarPath;
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static ScriptLoader getInstance(){
|
||||
@@ -39,12 +93,18 @@ public class ScriptLoader{
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
// class DynamicLoader extends ClassLoader{
|
||||
// public Class<Script> loadScript(String fullClassName, byte[] classData){
|
||||
// return (Class<Script>) defineClass(fullClassName,
|
||||
// classData, 0, classData.length);
|
||||
// }
|
||||
// }
|
||||
class DynamicLoader extends ClassLoader{
|
||||
public DynamicLoader(){}
|
||||
|
||||
public DynamicLoader(ClassLoader parent){
|
||||
super(parent);
|
||||
}
|
||||
|
||||
public Class<Script> loadScript(String fullClassName, byte[] classData){
|
||||
return (Class<Script>) defineClass(fullClassName,
|
||||
classData, 0, classData.length);
|
||||
}
|
||||
}
|
||||
|
||||
public void init(ScriptInfoService scriptInfoService) throws Exception {
|
||||
List<ScriptInfo> scriptInfoList = scriptInfoService.findAllScriptInfos();
|
||||
@@ -56,15 +116,21 @@ public class ScriptLoader{
|
||||
public void load(String scriptName, String scriptCode) throws Exception {
|
||||
String source = scriptCode;
|
||||
String fullPackage = DYNAMIC_BASE_PACKAGE + "." + scriptName;
|
||||
InMemoryJavaCompiler compiler = InMemoryJavaCompiler.newInstance();
|
||||
compiler.useParentClassLoader(getClass().getClassLoader());
|
||||
if(StringUtils.isNotBlank(classPath)) {
|
||||
compiler.useOptions("-classpath", classPath);
|
||||
}
|
||||
|
||||
Class<Script> cls = (Class<Script>) InMemoryJavaCompiler.newInstance().compile(fullPackage, source);
|
||||
Class<Script> cls = (Class<Script>) compiler.compile(fullPackage, source);
|
||||
|
||||
// Script newInstance = cls.getDeclaredConstructor().newInstance();
|
||||
// String xx = newInstance.postProcess("abc","ddd");
|
||||
// System.out.println(xx);
|
||||
this.loadedClass.put(scriptName, cls);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void unLoad(String scriptName) throws ClassNotFoundException, IOException {
|
||||
new File(path + DYNAMIC_BASE_PATH + "/" + scriptName +".class").delete();
|
||||
this.loadedClass.remove(scriptName);
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
<label class="col-sm-3 col-form-label">스크립트 명</label>
|
||||
<div class="col-sm-9">
|
||||
<select class="form-control select2bs4" th:field="*{scriptName}" th:value="*{scriptName}"> style="width: 100%;" >
|
||||
<option value="">선택안함</option>
|
||||
<option th:each="scriptInfo : ${scriptInfos}" th:value="${scriptInfo.scriptName}" th:text="${scriptInfo.scriptName + '(' + scriptInfo.scriptDesc + ')'}"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user