스크립트 기능 추가
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package com.eactive.httpmockserver;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
@@ -7,6 +8,8 @@ import java.util.Random;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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.JsonPath;
|
||||
|
||||
@@ -21,6 +24,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@@ -41,7 +45,7 @@ public class ApiController {
|
||||
//@RequestMapping(value = "/mock-api/**")
|
||||
@RequestMapping(value = "/{_:^(?!plugins|dist|favicon.ico).*$}/**")
|
||||
public ResponseEntity<String> mockApi(HttpEntity<String> httpEntity, HttpServletRequest request,
|
||||
HttpServletResponse response) {
|
||||
HttpServletResponse response) throws Exception {
|
||||
logger.info("==================================================");
|
||||
logger.info("==================================================");
|
||||
logger.info(request.getServletPath());
|
||||
@@ -119,6 +123,14 @@ public class ApiController {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return new ResponseEntity<String>(responseStr, responseHeaders, responseStatusCode);
|
||||
|
||||
@@ -43,6 +43,9 @@ public class ApiInfo implements Serializable {
|
||||
@Column(length = 10)
|
||||
private String method;
|
||||
|
||||
@Column(length = 500)
|
||||
private String scriptName;
|
||||
|
||||
@Column(length = 50)
|
||||
private String responseContentType;
|
||||
|
||||
@@ -181,6 +184,14 @@ public class ApiInfo implements Serializable {
|
||||
this.syntaxResponseBody2 = syntaxResponseBody2;
|
||||
}
|
||||
|
||||
public String getScriptName() {
|
||||
return scriptName;
|
||||
}
|
||||
|
||||
public void setScriptName(String scriptName) {
|
||||
this.scriptName = scriptName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user