스크립트 기능 추가
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.eactive.httpmockserver;
|
package com.eactive.httpmockserver;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -7,6 +8,8 @@ import java.util.Random;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import com.eactive.httpmockserver.script.Script;
|
||||||
|
import com.eactive.httpmockserver.script.ScriptLoader;
|
||||||
import com.jayway.jsonpath.DocumentContext;
|
import com.jayway.jsonpath.DocumentContext;
|
||||||
import com.jayway.jsonpath.JsonPath;
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
|
||||||
@@ -21,6 +24,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
@@ -41,7 +45,7 @@ public class ApiController {
|
|||||||
//@RequestMapping(value = "/mock-api/**")
|
//@RequestMapping(value = "/mock-api/**")
|
||||||
@RequestMapping(value = "/{_:^(?!plugins|dist|favicon.ico).*$}/**")
|
@RequestMapping(value = "/{_:^(?!plugins|dist|favicon.ico).*$}/**")
|
||||||
public ResponseEntity<String> mockApi(HttpEntity<String> httpEntity, HttpServletRequest request,
|
public ResponseEntity<String> mockApi(HttpEntity<String> httpEntity, HttpServletRequest request,
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response) throws Exception {
|
||||||
logger.info("==================================================");
|
logger.info("==================================================");
|
||||||
logger.info("==================================================");
|
logger.info("==================================================");
|
||||||
logger.info(request.getServletPath());
|
logger.info(request.getServletPath());
|
||||||
@@ -119,6 +123,14 @@ public class ApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String responseStr = assignResponseBody(api);
|
String responseStr = assignResponseBody(api);
|
||||||
|
|
||||||
|
String scriptName = api.getScriptName();
|
||||||
|
if(StringUtils.isNotBlank(scriptName)) {
|
||||||
|
Script script = ScriptLoader.getInstance().newScript(scriptName);
|
||||||
|
logger.info("Execute postProcess Script={}", scriptName);
|
||||||
|
|
||||||
|
responseStr = script.postProcess(httpEntity.getBody(), responseStr);
|
||||||
|
}
|
||||||
logger.info("ResponseBody={}", responseStr);
|
logger.info("ResponseBody={}", responseStr);
|
||||||
|
|
||||||
return new ResponseEntity<String>(responseStr, responseHeaders, responseStatusCode);
|
return new ResponseEntity<String>(responseStr, responseHeaders, responseStatusCode);
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ public class ApiInfo implements Serializable {
|
|||||||
@Column(length = 10)
|
@Column(length = 10)
|
||||||
private String method;
|
private String method;
|
||||||
|
|
||||||
|
@Column(length = 500)
|
||||||
|
private String scriptName;
|
||||||
|
|
||||||
@Column(length = 50)
|
@Column(length = 50)
|
||||||
private String responseContentType;
|
private String responseContentType;
|
||||||
|
|
||||||
@@ -181,6 +184,14 @@ public class ApiInfo implements Serializable {
|
|||||||
this.syntaxResponseBody2 = syntaxResponseBody2;
|
this.syntaxResponseBody2 = syntaxResponseBody2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getScriptName() {
|
||||||
|
return scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScriptName(String scriptName) {
|
||||||
|
this.scriptName = scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ApiInfo{" + "id=" + id + ", apiGroupName='" + apiGroupName + '\'' + ", apiName='" + apiName + '\''
|
return "ApiInfo{" + "id=" + id + ", apiGroupName='" + apiGroupName + '\'' + ", apiName='" + apiName + '\''
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
public interface Script {
|
||||||
|
|
||||||
|
public String postProcess(String requestMessage, String responseMessage) throws Exception;
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import com.eactive.httpmockserver.ApiManageRestController;
|
||||||
|
import com.eactive.httpmockserver.datatables.DataTablesRequest;
|
||||||
|
import com.eactive.httpmockserver.datatables.DataTablesResponse;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.ExampleMatcher;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.BindingResult;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class ScriptController {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ScriptInfoService scriptInfoService;
|
||||||
|
|
||||||
|
@GetMapping("/manage/findAllScriptInfos")
|
||||||
|
public String getAllScriptInfoForDatatables(Pageable pageable, Model model) {
|
||||||
|
return "page/list-script-info";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = { "/manage/getScriptInfoForm", "/manage/getScriptInfoForm/{id}" })
|
||||||
|
public String getScriptInfoForm(Model model, @PathVariable Optional<Long> id) throws IOException {
|
||||||
|
if (id.isPresent()) {
|
||||||
|
Optional<ScriptInfo> optional = scriptInfoService.findById(id.get());
|
||||||
|
if (optional.isPresent()) {
|
||||||
|
model.addAttribute("scriptInfo", optional.get());
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("No data, id: " + id.get());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ScriptInfo spec = new ScriptInfo();
|
||||||
|
InputStream is = getClass().getClassLoader().getResourceAsStream("basecodes/BaseScript.java.template");
|
||||||
|
String baseCode = IOUtils.toString(is, "utf-8");
|
||||||
|
spec.setScriptCode(baseCode);
|
||||||
|
model.addAttribute("scriptInfo", spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "page/add-edit-script-info";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/manage/saveScriptInfo")
|
||||||
|
public String saveScriptInfo(@Valid ScriptInfo spec, BindingResult result, Model model) throws IOException, ClassNotFoundException {
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
return "page/add-edit-script-info";
|
||||||
|
}
|
||||||
|
ScriptLoader.getInstance().load(spec.getScriptName(), spec.getScriptCode());
|
||||||
|
if (spec.getId() == null) {
|
||||||
|
scriptInfoService.save(spec);
|
||||||
|
} else {
|
||||||
|
scriptInfoService.update(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BooleanUtils.toBoolean(spec.getUseYn())) {
|
||||||
|
// 기존설정 useYn=N으로 수정
|
||||||
|
scriptInfoService.setUnuseOthers(spec.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return "redirect:/manage/findAllScriptInfos";
|
||||||
|
}
|
||||||
|
@RequestMapping("/manage/deleteScriptInfo/{id}")
|
||||||
|
public String deleteScriptInfo(@PathVariable Long id) throws IOException, ClassNotFoundException {
|
||||||
|
if (id == null) {
|
||||||
|
throw new IllegalArgumentException("No data, id: " + id);
|
||||||
|
}
|
||||||
|
String scriptName = scriptInfoService.findById(id).get().getScriptName();
|
||||||
|
ScriptLoader.getInstance().unLoad(scriptName);
|
||||||
|
scriptInfoService.delete(id);
|
||||||
|
|
||||||
|
return "redirect:/manage/findAllScriptInfos";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/manage/rest/scriptInfos")
|
||||||
|
@ResponseBody
|
||||||
|
public DataTablesResponse<ScriptInfo> getAllScriptInfoForDatatables(@RequestBody DataTablesRequest req) {
|
||||||
|
Pageable pageable = ApiManageRestController.assignJpaPageable(req);
|
||||||
|
|
||||||
|
// search option
|
||||||
|
Example<ScriptInfo> example = null;
|
||||||
|
if (StringUtils.isNotBlank(req.getSearch().getValue())) {
|
||||||
|
ScriptInfo spec = new ScriptInfo();
|
||||||
|
spec.setScriptName(req.getSearch().getValue());
|
||||||
|
|
||||||
|
ExampleMatcher matcher = ExampleMatcher.matchingAny().withMatcher("specName",
|
||||||
|
new ExampleMatcher.GenericPropertyMatcher().contains().ignoreCase());
|
||||||
|
example = Example.of(spec, matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTablesResponse<ScriptInfo> res = new DataTablesResponse<ScriptInfo>(
|
||||||
|
scriptInfoService.findAllScriptInfos(example, pageable));
|
||||||
|
res.setRecordsFiltered(scriptInfoService.getRecordsCount(example));
|
||||||
|
res.setRecordsTotal(scriptInfoService.getRecordsCount(null));
|
||||||
|
res.setDraw(req.getDraw());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class ScriptInfo implements Serializable {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Column(length = 500, nullable = false)
|
||||||
|
private String scriptName;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Column(length = 4000, nullable = false)
|
||||||
|
private String scriptDesc;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Lob
|
||||||
|
private String scriptCode;
|
||||||
|
|
||||||
|
@Column(length = 1)
|
||||||
|
private String useYn;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScriptName() {
|
||||||
|
return scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScriptName(String scriptName) {
|
||||||
|
this.scriptName = scriptName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScriptDesc() {
|
||||||
|
return scriptDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScriptDesc(String scriptDesc) {
|
||||||
|
this.scriptDesc = scriptDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScriptCode() {
|
||||||
|
return scriptCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScriptCode(String scriptCode) {
|
||||||
|
this.scriptCode = scriptCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUseYn() {
|
||||||
|
return useYn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseYn(String useYn) {
|
||||||
|
this.useYn = useYn;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import com.eactive.httpmockserver.socket.BytesMessageSpec;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface ScriptInfoDao extends JpaRepository<ScriptInfo, Long> {
|
||||||
|
ScriptInfo findFirstByUseYn(String useYn);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query(value = "update ScriptInfo b set b.useYn='N' where b.id <> ?1")
|
||||||
|
void updateUnuseOthers(Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface ScriptInfoService {
|
||||||
|
public ScriptInfo findActiveScriptInfo();
|
||||||
|
|
||||||
|
public List<ScriptInfo> findAllScriptInfos(Pageable pageable);
|
||||||
|
|
||||||
|
public List<ScriptInfo> findAllScriptInfos(Example<ScriptInfo> example, Pageable pageable);
|
||||||
|
|
||||||
|
public long getRecordsCount(Example<ScriptInfo> example);
|
||||||
|
|
||||||
|
public Optional<ScriptInfo> findById(Long id);
|
||||||
|
|
||||||
|
public ScriptInfo save(ScriptInfo spec);
|
||||||
|
|
||||||
|
public ScriptInfo update(ScriptInfo spec);
|
||||||
|
|
||||||
|
public void delete(Long id);
|
||||||
|
|
||||||
|
public void setUnuseOthers(Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(propagation = Propagation.SUPPORTS)
|
||||||
|
public class ScriptInfoServiceImpl implements ScriptInfoService {
|
||||||
|
@Autowired
|
||||||
|
private ScriptInfoDao dao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScriptInfo findActiveScriptInfo() {
|
||||||
|
return dao.findFirstByUseYn("Y");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ScriptInfo> findAllScriptInfos(Pageable pageable) {
|
||||||
|
Page<ScriptInfo> pagedResult = dao.findAll(pageable);
|
||||||
|
if (pagedResult.hasContent()) {
|
||||||
|
return pagedResult.getContent();
|
||||||
|
} else {
|
||||||
|
return new ArrayList<ScriptInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ScriptInfo> findAllScriptInfos(Example<ScriptInfo> example, Pageable pageable) {
|
||||||
|
if (example == null) {
|
||||||
|
return findAllScriptInfos(pageable);
|
||||||
|
}
|
||||||
|
|
||||||
|
Page<ScriptInfo> pagedResult = dao.findAll(example, pageable);
|
||||||
|
if (pagedResult.hasContent()) {
|
||||||
|
return pagedResult.getContent();
|
||||||
|
} else {
|
||||||
|
return new ArrayList<ScriptInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ScriptInfo> findById(Long id) {
|
||||||
|
return dao.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRecordsCount(Example<ScriptInfo> example) {
|
||||||
|
if (example == null) {
|
||||||
|
return dao.count();
|
||||||
|
} else {
|
||||||
|
return dao.count(example);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
|
||||||
|
public ScriptInfo save(ScriptInfo spec) {
|
||||||
|
return dao.save(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
|
||||||
|
public ScriptInfo update(ScriptInfo spec) {
|
||||||
|
return dao.save(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
|
||||||
|
public void delete(Long id) {
|
||||||
|
dao.deleteById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
|
||||||
|
public void setUnuseOthers(Long id) {
|
||||||
|
dao.updateUnuseOthers(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.eactive.httpmockserver.script;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ScriptLoader{
|
||||||
|
String path = ScriptLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
|
||||||
|
public static String DYNAMIC_BASE_PACKAGE = "com.eactive.httpmockserver.script.dynamic";
|
||||||
|
public static String DYNAMIC_BASE_PATH = DYNAMIC_BASE_PACKAGE.replaceAll("[.]","/");
|
||||||
|
|
||||||
|
private static final ScriptLoader instance = new ScriptLoader();
|
||||||
|
private Map<String, Class<Script>> loadedClass;
|
||||||
|
|
||||||
|
private ScriptLoader(){
|
||||||
|
this.loadedClass = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScriptLoader getInstance(){
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Script newScript(String scriptName) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||||
|
Class<Script> scriptClass = this.loadedClass.get(scriptName);
|
||||||
|
Script newInstance = scriptClass.getDeclaredConstructor().newInstance();
|
||||||
|
return newInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DynamicLoader extends ClassLoader{
|
||||||
|
public Class<Script> loadScript(String fullClassName, byte[] classData){
|
||||||
|
return (Class<Script>) defineClass(fullClassName,
|
||||||
|
classData, 0, classData.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(String scriptName, String scriptCode) throws ClassNotFoundException, IOException {
|
||||||
|
String source = scriptCode;
|
||||||
|
String fullPath = DYNAMIC_BASE_PATH + "/" + scriptName;
|
||||||
|
String fullPackage = DYNAMIC_BASE_PACKAGE + "." + scriptName;
|
||||||
|
|
||||||
|
|
||||||
|
File packagePath = new File(path + DYNAMIC_BASE_PATH);
|
||||||
|
if(!packagePath.exists())
|
||||||
|
packagePath.mkdirs();
|
||||||
|
|
||||||
|
File sourceFile = new File(path + fullPath + ".java");
|
||||||
|
new FileWriter(sourceFile).append(source).close();
|
||||||
|
|
||||||
|
// 만들어진 Java 파일을 컴파일한다.
|
||||||
|
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||||
|
compiler.run(null, System.out, System.out, sourceFile.getPath());
|
||||||
|
|
||||||
|
|
||||||
|
// File classPath = new File(path + DYNAMIC_BASE_PATH);
|
||||||
|
|
||||||
|
// 컴파일된 Class를 Load
|
||||||
|
// URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {classPath.toURI().toURL()});
|
||||||
|
// Class<Script> cls = (Class<Script>) Class.forName(fullPackage, true, classLoader);
|
||||||
|
// new File(path + DYNAMIC_BASE_PATH + "/" + scriptName +".class").delete();
|
||||||
|
// sourceFile.delete();
|
||||||
|
File classFile = new File(path + DYNAMIC_BASE_PATH + "/" + scriptName +".class");
|
||||||
|
byte[] classData = IOUtils.toByteArray(new FileInputStream(classFile));
|
||||||
|
DynamicLoader dynamicLoader = new DynamicLoader();
|
||||||
|
Class<Script> cls = dynamicLoader.loadScript(fullPackage, classData);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ logging:
|
|||||||
level:
|
level:
|
||||||
root: info
|
root: info
|
||||||
server:
|
server:
|
||||||
port.http: 38080
|
port.http: 48080
|
||||||
port: 38443
|
port: 38443
|
||||||
ssl:
|
ssl:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.eactive.httpmockserver.script.dynamic;
|
||||||
|
import com.eactive.httpmockserver.script.Script;
|
||||||
|
|
||||||
|
public class implements Script{
|
||||||
|
@Override
|
||||||
|
public String postProcess(String requestMessage, String responseMessage) throws Exception {
|
||||||
|
return responseMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -81,6 +81,22 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="#" class="nav-link">
|
||||||
|
<i class="nav-icon fas fa-edit"></i>
|
||||||
|
<p>
|
||||||
|
부가 기능 <i class="fas fa-angle-left right"></i>
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
<ul class="nav nav-treeview">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a th:href="@{/manage/findAllScriptInfos}" class="nav-link">
|
||||||
|
<i class="far fa-circle nav-icon"></i>
|
||||||
|
<p>Script 관리</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<!-- /.sidebar-menu -->
|
<!-- /.sidebar-menu -->
|
||||||
|
|||||||
@@ -153,6 +153,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-3 col-form-label">스크립트 명</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" th:field="*{scriptName}"
|
||||||
|
class="form-control"
|
||||||
|
placeholder="수행 스크립트 명">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorator="layout/default_layout">
|
||||||
|
|
||||||
|
<div class="content-wrapper" layout:fragment="content">
|
||||||
|
<!-- Content Header (Page header) -->
|
||||||
|
<section class="content-header">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h1 th:if="${scriptInfo.id == null}">스크립트 정보 등록</h1>
|
||||||
|
<h1 th:unless="${scriptInfo.id == null}">스크립트 정보 수정</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<ol class="breadcrumb float-sm-right">
|
||||||
|
<li class="breadcrumb-item"><a href="#">Home</a></li>
|
||||||
|
<li class="breadcrumb-item active">스크립트 관리</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content" style="padding-bottom: 1rem;">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<form id="form" class="inputForm" action="#" th:action="@{/manage/saveScriptInfo}"
|
||||||
|
th:object="${scriptInfo}" method="post">
|
||||||
|
<div class="card card-default">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Basic Info</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-3 col-form-label">스크립트 명<small>(*)</small></label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="scriptName" type="text" th:field="*{scriptName}" class="form-control">
|
||||||
|
<span th:if="${#fields.hasErrors('scriptName')}"
|
||||||
|
th:errors="*{scriptName}" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-3 col-form-label">스크립트 설명<small>(*)</small></label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input id="scriptName" type="text" th:field="*{scriptDesc}" class="form-control">
|
||||||
|
<span th:if="${#fields.hasErrors('scriptDesc')}"
|
||||||
|
th:errors="*{scriptDesc}" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-default">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Script Code</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-1 col-form-label">Script Editor</label>
|
||||||
|
<div class="col-sm-11">
|
||||||
|
<div id="scriptCode" class="shadow-sm" style="height:400px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" th:field="*{id}">
|
||||||
|
<button type="submit" class="btn btn-primary mr-2">Submit</button>
|
||||||
|
<button type="button" class="btn btn-secondary"
|
||||||
|
onclick="history.go(-1);">Cancel</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<th:block layout:fragment="script">
|
||||||
|
<link rel="stylesheet" data-name="vs/editor/editor.main"
|
||||||
|
th:href="@{/plugins/vs/editor/editor.main.css}">
|
||||||
|
|
||||||
|
<script>var require = { paths: { 'vs': '../../plugins/vs' } };</script>
|
||||||
|
|
||||||
|
<script th:src="@{/plugins/vs/loader.js}"></script>
|
||||||
|
<script th:src="@{/plugins/vs/editor/editor.main.nls.js}"></script>
|
||||||
|
<script th:src="@{/plugins/vs/editor/editor.main.js}"></script>
|
||||||
|
<script th:inline="javascript">
|
||||||
|
var urlForSidebar = "/manage/findAllScriptInfos";
|
||||||
|
let editor
|
||||||
|
const isNew = [[${scriptInfo.id == null}]];
|
||||||
|
function setMonaco(elementId, initValue){
|
||||||
|
var targetDivElement = document.getElementById(elementId);
|
||||||
|
editor = monaco.editor.create(targetDivElement, {
|
||||||
|
value: initValue,
|
||||||
|
language: 'java',
|
||||||
|
minimap: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
wordWrap: 'on',
|
||||||
|
wrappingStrategy: 'simple',
|
||||||
|
wordWrapBreakAfterCharacters: '',
|
||||||
|
wordWrapBreakBeforeCharacters: '',
|
||||||
|
wrappingIndent: 'none',
|
||||||
|
automaticLayout: true
|
||||||
|
});
|
||||||
|
const readonlyRange = new monaco.Range (1, 0, 7, 0)
|
||||||
|
editor.onKeyDown (e => {
|
||||||
|
const contains = editor.getSelections().findIndex (range => readonlyRange.intersectRanges (range))
|
||||||
|
let currentLineNumber = editor.getPosition().lineNumber
|
||||||
|
let lineCount = editor.getModel().getLineCount();
|
||||||
|
console.log(e.ctrlKey)
|
||||||
|
if(e.ctrlKey && e.keyCode == 33){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch(e.keyCode){
|
||||||
|
case 13:
|
||||||
|
case 14:
|
||||||
|
case 15:
|
||||||
|
case 16:
|
||||||
|
case 17:
|
||||||
|
case 18:
|
||||||
|
case 63:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchTailPart = lineCount == currentLineNumber || lineCount - 1 == currentLineNumber;
|
||||||
|
if (contains !== -1 || matchTailPart) {
|
||||||
|
e.stopPropagation ()
|
||||||
|
e.preventDefault () // for Ctrl+C, Ctrl+V
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#form').submit(function(eventObj) {
|
||||||
|
$('#scriptName').prop( "disabled", false );
|
||||||
|
$("<input />").attr("type", "hidden")
|
||||||
|
.attr("name", elementId)
|
||||||
|
.attr("value", editor.getValue())
|
||||||
|
.appendTo("#form");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setMonaco('scriptCode', [[${scriptInfo.scriptCode}]]);
|
||||||
|
if(isNew == false){
|
||||||
|
$('#scriptName').prop( "disabled", true );
|
||||||
|
}
|
||||||
|
$('#scriptName').on("change keyup paste", function(){
|
||||||
|
const scriptName = $('#scriptName').val();
|
||||||
|
const currentTextLength = editor.getModel().getLinesContent()[3].length + 1;
|
||||||
|
const newText = `public class ${scriptName} implements Script{`;
|
||||||
|
editor.executeEdits("my-source",
|
||||||
|
[{ range: new monaco.Range(4, 1, 4, currentTextLength), text: newText, forceMoveMarkers: true }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:th="http://www.thymeleaf.org"
|
||||||
|
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||||
|
layout:decorator="layout/default_layout">
|
||||||
|
|
||||||
|
<th:block layout:fragment="css">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
th:href="@{/plugins/datatables-bs4/css/dataTables.bootstrap4.min.css}">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
th:href="@{/plugins/datatables-responsive/css/responsive.bootstrap4.min.css}">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
th:href="@{/plugins/datatables-buttons/css/buttons.bootstrap4.min.css}">
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<div class="content-wrapper" layout:fragment="content">
|
||||||
|
<!-- Content Header (Page header) -->
|
||||||
|
<section class="content-header">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<h1>스크립트 관리</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<ol class="breadcrumb float-sm-right">
|
||||||
|
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
||||||
|
<li class="breadcrumb-item active">스크립트 관리</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
|
<section class="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">응답 송신 전 수행 할 스크립트, GUID 복제나 특정 예외전문등의 처리 수행 로직을 작성한다(java)</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<table id="listApiTable"
|
||||||
|
class="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>스크립트 명</th>
|
||||||
|
<th>스크립트 설명</th>
|
||||||
|
<th>수정</th>
|
||||||
|
<th>삭제</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-body -->
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.col -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
<!-- /.container-fluid -->
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<th:block layout:fragment="script">
|
||||||
|
<script th:src="@{/plugins/datatables/jquery.dataTables.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-bs4/js/dataTables.bootstrap4.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-responsive/js/dataTables.responsive.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-responsive/js/responsive.bootstrap4.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-buttons/js/dataTables.buttons.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-buttons/js/buttons.bootstrap4.min.js}"></script>
|
||||||
|
<script th:src="@{/plugins/jszip/jszip.min.js}"></script>
|
||||||
|
<script th:src="@{/plugins/pdfmake/pdfmake.min.js}"></script>
|
||||||
|
<script th:src="@{/plugins/pdfmake/vfs_fonts.js}"></script>
|
||||||
|
<script th:src="@{/plugins/datatables-buttons/js/buttons.html5.min.js}"></script>
|
||||||
|
<script th:src="@{/plugins/datatables-buttons/js/buttons.print.min.js}"></script>
|
||||||
|
<script
|
||||||
|
th:src="@{/plugins/datatables-buttons/js/buttons.colVis.min.js}"></script>
|
||||||
|
|
||||||
|
<script th:inline="javascript">
|
||||||
|
$.fn.dataTable.ext.buttons.new = {
|
||||||
|
text: 'New',
|
||||||
|
action: function ( e, dt, node, config ) {
|
||||||
|
location.href = "/manage/getScriptInfoForm";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listApiTable = $("#listApiTable").DataTable({
|
||||||
|
"responsive": true, "lengthChange": false, "autoWidth": false,
|
||||||
|
"processing": true,
|
||||||
|
"serverSide": true,
|
||||||
|
"pageLength": 50,
|
||||||
|
"searchDelay": 1200,
|
||||||
|
"ajax": {
|
||||||
|
"url": "/manage/rest/scriptInfos",
|
||||||
|
"type": "POST",
|
||||||
|
"dataType": "json",
|
||||||
|
"contentType": "application/json",
|
||||||
|
"data": function (d) {
|
||||||
|
return JSON.stringify(d);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"columns": [
|
||||||
|
{"data": "scriptName"},
|
||||||
|
{"data": "scriptDesc"},
|
||||||
|
{"data": "id", "searchable": false, "orderable": false},
|
||||||
|
{"data": "id", "searchable": false, "orderable": false}
|
||||||
|
],
|
||||||
|
"columnDefs": [
|
||||||
|
{
|
||||||
|
"render": function (data, type, row) {
|
||||||
|
return '<a href="/manage/getScriptInfoForm/' + data + '" class="btn btn-primary"><i class="fas fa-edit"></i></a>';
|
||||||
|
},
|
||||||
|
"targets": -2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"render": function (data, type, row) {
|
||||||
|
var msg = "'정말로 데이터를 삭제 하시겠습니까?'";
|
||||||
|
return '<a href="/manage/deleteScriptInfo/' + data + '" onclick="return confirm(' + msg +');" class="btn btn-primary"><i class="fas fa-trash"></i></a>';
|
||||||
|
},
|
||||||
|
"targets": -1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"buttons": ["new", "excel", "colvis"],
|
||||||
|
"initComplete": function() {
|
||||||
|
listApiTable.buttons().container().appendTo('#listApiTable_wrapper .col-md-6:eq(0)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
$(".dataTables_filter input").unbind().bind("input", function(e) { // Bind our desired behavior
|
||||||
|
// If the length is 3 or more characters, or the user pressed ENTER, search
|
||||||
|
if(this.value.length >= 3 || e.keyCode == 13) {
|
||||||
|
// Call the API search function
|
||||||
|
listApiTable.search(this.value).draw();
|
||||||
|
}
|
||||||
|
// Ensure we clear the search if they backspace far enough
|
||||||
|
if(this.value == "") {
|
||||||
|
listApiTable.search("").draw();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
</script>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user