몇일 전 Java 문법으로 된 문자열을 입력받아서 해당 Logic을 수행 할 수 있는 방안에 대해 검토를 해달라는 요청이 들어와서 이것저것 확인 하던 중 방법을 알아내어 정리하고자 합니다.
Java는 Script 언어와 달리 컴파일이라는 과정을 거쳐야지만 비로서 실행 할 수 있는 코드로 변한 되는데요..
문자열로 된 자바 문법을 실행하기 위해서는 이 컴파일이라는 과정을 거쳐야 하고, 요구사항대로 처리하기 위해서는
프로그램이 동작하는 도중에 특정 소스를 Java 파일로 만들고 컴파일까지 한 후 Class를 Load해야 합니다.
나머진 뭐 가능하겠지 생각했지만 프로그램 실행중에 컴파일을 할 수 있나? 라는 생각이 제일 먼저 떠올랐구요, 그래서 우리의 구글신님께 물어본 결과 역시나 JDK에서 기본적으로 제공을 하고 있더군요... 바로 JavaCompiler라는 놈입니다. 이놈을 가지고 예제 소스를 구현해 보았습니다.
먼저 소스 파일을 만들고 실행하는 유틸리티용 Class입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 |
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/** * Created by sunghun on 2014-12-30. */
public class DynamicClassBuilder {
public Object createInstance(String body) throws Exception {
// 프로젝트 Home Directory 경로 조회
String path = StartMain.class.getProtectionDomain().getCodeSource().getLocation().getPath();
// Source를 만들고 Java파일 생성
File sourceFile = new File(path + "example/DynamicClass.java");
String source = this.getSource(body);
System.out.println(source);
new FileWriter(sourceFile).append(source).close();
// 만들어진 Java 파일을 컴파일한다.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, System.out, System.out, sourceFile.getPath());
// 컴파일된 Class를 Load
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {new File(path + "example").toURI().toURL()});
Class<?> cls = Class.forName("example.DynamicClass", true, classLoader);
// Load한 Class의 Instance를 생성
return cls.newInstance();
}
public byte[] runObject(Object obj, byte[] params) throws Exception {
String methodName = "runMethod";
Class arguments[] = new Class[] {params.getClass()};
// Source를 만들때 지정한 Method를 실행
Method objMethod = obj.getClass().getMethod(methodName, arguments);
Object result = objMethod.invoke(obj, params);
return (byte[])result;
}
private String getSource(String body) {
StringBuffer sb = new StringBuffer();
// Java Source를 생성한다.
sb.append("package example; public class DynamicClass { public byte[] runMethod(byte[] params) throws Exception {")
.append(body)
.append("} }");
return sb.toString();
}
} |
cs |
실행할 Main Class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 |
import java.net.URI;
import java.net.URL;
/** * Created by sunghun on 2014-12-30. */
public class StartMain {
public static void main(String[] args) throws Exception {
DynamicClassBuilder dcb = new DynamicClassBuilder();
// 문자열로 전달 받은 실행할 Java 문법
String body = "byte[] result = new byte[1]; if(params[0] == 't') result[0] = 'a'; else result[0] = 'b'; return result;";
// 위 문자열을 기준으로 생성된 Class를 Object로 생성
Object obj = dcb.createInstance(body);
// 실행할 Method에 전달할 파라미터 셋팅. 테스트 데이터
byte params[] = new byte[] {'t', 'e', 's', 't'};
// 실행 후 결과 전달 받음
byte[] rst = dcb.runObject(obj, params);
// 실행 결과 출력
System.out.print("byte result : ");
if (rst != null) {
for (byte b : rst) {
System.out.print((char)b + ", ");
}
}
}
} |
cs |
그런데 요청 정보에 Java Logic을 받아서 실행을 시키고자 하는것은 설계를 아주 잘 해놓아야 할것으로 보이네요..
전달받은 Java 문법에 오류가 있거나 하면 서버가 죽어버리는 사태가 발생합니다.
이 부분을 얼마나 안전하게 돌아가게씀 설계를 해야 하는것인지가 관건인거 같네요..
아무튼 Java로 이런식의 처리도 가능하다는 점이 중요한 것이니 참고하면 되겠습니다.
[출처]http://blog.naver.com/special9486/220227329182
'개발 > JAVA' 카테고리의 다른 글
[자바강좌][java응용]네트워크 게임 만들기 (0) | 2009.12.09 |
---|---|
[자바강좌][java응용]네트워크 프로그래밍 (0) | 2009.12.08 |
[자바강좌][java입문]Java2D Graphics (0) | 2009.12.08 |
[자바강좌][java입문]AWT Event 처리 (0) | 2009.12.08 |
[자바강좌][java입문]AWT 프로그래밍 (0) | 2009.12.08 |