boot jar 동적컴파일 대응
This commit is contained in:
@@ -22,6 +22,7 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
|
||||||
// implementation 'com.github.darrachequesne:spring-data-jpa-datatables:5.1.0'
|
// 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 'org.apache.commons:commons-lang3:3.10'
|
||||||
implementation 'commons-io:commons-io:2.6'
|
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.ScriptInfoService;
|
||||||
import com.eactive.httpmockserver.script.ScriptLoader;
|
import com.eactive.httpmockserver.script.ScriptLoader;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class PostConstructBean {
|
public class PostConstructBean {
|
||||||
@@ -17,7 +21,7 @@ public class PostConstructBean {
|
|||||||
public ScriptInfoService scriptInfoService;
|
public ScriptInfoService scriptInfoService;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() throws URISyntaxException {
|
||||||
logger.info("init scripts");
|
logger.info("init scripts");
|
||||||
try {
|
try {
|
||||||
ScriptLoader.getInstance().init(scriptInfoService);
|
ScriptLoader.getInstance().init(scriptInfoService);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package com.eactive.httpmockserver.script;
|
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.mdkt.compiler.InMemoryJavaCompiler;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.tools.JavaCompiler;
|
import javax.tools.*;
|
||||||
import javax.tools.ToolProvider;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ScriptLoader{
|
public class ScriptLoader{
|
||||||
String path = ScriptLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
String path = ScriptLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||||
@@ -24,9 +24,63 @@ public class ScriptLoader{
|
|||||||
|
|
||||||
private static final ScriptLoader instance = new ScriptLoader();
|
private static final ScriptLoader instance = new ScriptLoader();
|
||||||
private Map<String, Class<Script>> loadedClass;
|
private Map<String, Class<Script>> loadedClass;
|
||||||
|
private String classPath = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BOOT 일 경우 JAR 내의 Class 목록이 BOOT-INF로 들어가서 컴파일시 참조가 안되는 문제가 있음
|
||||||
|
* 임시로 /tmp 폴더에 압축해제하여 해당 경로를 참조하게 하였으나 메모리 내에서 참조가능거나 jar파일을 직접 참조 할 수 있는 방안을 찾아볼 예정임
|
||||||
|
* Groovy 스크립트로 변경도 검토 중
|
||||||
|
*/
|
||||||
private ScriptLoader(){
|
private ScriptLoader(){
|
||||||
this.loadedClass = new HashMap<>();
|
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(){
|
public static ScriptLoader getInstance(){
|
||||||
@@ -39,12 +93,18 @@ public class ScriptLoader{
|
|||||||
return newInstance;
|
return newInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// class DynamicLoader extends ClassLoader{
|
class DynamicLoader extends ClassLoader{
|
||||||
// public Class<Script> loadScript(String fullClassName, byte[] classData){
|
public DynamicLoader(){}
|
||||||
// return (Class<Script>) defineClass(fullClassName,
|
|
||||||
// classData, 0, classData.length);
|
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 {
|
public void init(ScriptInfoService scriptInfoService) throws Exception {
|
||||||
List<ScriptInfo> scriptInfoList = scriptInfoService.findAllScriptInfos();
|
List<ScriptInfo> scriptInfoList = scriptInfoService.findAllScriptInfos();
|
||||||
@@ -56,15 +116,21 @@ public class ScriptLoader{
|
|||||||
public void load(String scriptName, String scriptCode) throws Exception {
|
public void load(String scriptName, String scriptCode) throws Exception {
|
||||||
String source = scriptCode;
|
String source = scriptCode;
|
||||||
String fullPackage = DYNAMIC_BASE_PACKAGE + "." + scriptName;
|
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");
|
// String xx = newInstance.postProcess("abc","ddd");
|
||||||
// System.out.println(xx);
|
// System.out.println(xx);
|
||||||
this.loadedClass.put(scriptName, cls);
|
this.loadedClass.put(scriptName, cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void unLoad(String scriptName) throws ClassNotFoundException, IOException {
|
public void unLoad(String scriptName) throws ClassNotFoundException, IOException {
|
||||||
new File(path + DYNAMIC_BASE_PATH + "/" + scriptName +".class").delete();
|
new File(path + DYNAMIC_BASE_PATH + "/" + scriptName +".class").delete();
|
||||||
this.loadedClass.remove(scriptName);
|
this.loadedClass.remove(scriptName);
|
||||||
|
|||||||
@@ -160,6 +160,7 @@
|
|||||||
<label class="col-sm-3 col-form-label">스크립트 명</label>
|
<label class="col-sm-3 col-form-label">스크립트 명</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select class="form-control select2bs4" th:field="*{scriptName}" th:value="*{scriptName}"> style="width: 100%;" >
|
<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>
|
<option th:each="scriptInfo : ${scriptInfos}" th:value="${scriptInfo.scriptName}" th:text="${scriptInfo.scriptName + '(' + scriptInfo.scriptDesc + ')'}"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user