검색
다른 검색어로 검색을 시도하시기 바랍니다.
BCEL로 런타임에 Arguments(매개변수들) 캡쳐하는 Instruction 추가하기.
Bcel 을 처음 다룰 당시에 만들었던 간단한 매개변수 출력하는 BCEL transform이다.
상당히 다듬어지지않은 막코드라서 감안해서 볼 필요가있다.
핵심부분은 밑줄과 주석을 달겠다.
이전글(http://blog.sanguneo.com/21) 에서 premain 을 이용한 javaagent 사용법은 간략하게 배웠는데.
BCEL을 여기에 접목시킨다는것은 조금 고차원적인 접근이 필요하다.(필자의 생각)
우선적으로 소스를 펼치기에 앞서 이론설명을 하자면 자바의 .class 파일은 완전하계 기계어(?)로 번역된것이 아닌,
그렇다고 소스형태도아닌 그 중간어딘가의 형태로 볼수있다.
javac 명령어를 실행하면 소스파일을 java컴파일하면 class 파일로 뱉어내게되는데, 이 class파일이 ByteCode의 목적집합코드인셈이다.(?)
길게 이야기할거없이 일단 class파일은 바이트코드로 되어있다고 보면 된다.
웹상의 대부분의 자료들(BCEL에 관련해서만) 은 BCEL을 이용한 정적인 BCI 기술을 보여준다.
정적인 BCI기술이라함은 class 파일화된 바이트코드를 다루는것이라 하겠다.
하지만! 이 정적인 BCI기술은 엄청난 한계가있는데,
상용프로그램에대한 라이센스문제(수정불가등의 조항), 묶여있는 클래스군 혹은 시스템클래스 에대한문제(rt.jar 등) 이 그것이다.
그렇다면 실시간으로 무엇을 어떻게 불러와서 사용할수 있을고 하니. 자바에서 기본적으로 Transform 을 지원한다.(since jdk1.5)
여기서 사용할것은 ClassFileTransformer 이다.
우선 클래스파일 트랜스포머 메소드는 아래처럼 implements 로 얻어와서 정의하여 사용한다.
주의) 소스가 동작하지않을 수 있음. 버그있으면 피드백 바랍니다. 원본 코드를 자꾸 수정해서 아래코드는 기억을 더듬어 작성했습니다.
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 53 54 55 56 57 58 59 60 | import java.lang.instrument.*; import java.security.*; import org.apache.bcel.generic.*; import org.apache.bcel.classfile.*; import org.apache.bcel.Constants; import org.apache.bcel.Constants.*; import org.apache.bcel.*; import java.io.*; public class MethodInstrument { public static void premain(String options, Instrumentation instrumentation) { instrumentation.addTransformer( new ex()); // 트랜스포머를 추가한다. 트랜스포머는 바로 아래에 정의 } public static class ex implements ClassFileTransformer { public byte [] transform(ClassLoader loader, String cn, Class classBeingRedefined, ProtectionDomain protectionDomain, byte [] classfileBuffer) throws IllegalClassFormatException { 자바독을 찾아보면 위와같이 정의하도록 되어있다. if (cn.startsWith( "javax" ) || cn.startsWith( "sun" )||cn.startsWith( "com" )) { return classfileBuffer; } // 그냥 통과할 클래스에대해 지정한다. try { ByteArrayInputStream bais = new ByteArrayInputStream(classfileBuffer); //스트림버퍼로부터 바이트코드를 불러온다(핵심) ClassParser parser = new ClassParser(bais, cn); JavaClass clazz = parser.parse(); ClassGen classGen = new ClassGen(clazz); InstructionFactory instructionFactory = new InstructionFactory(classGen); InstructionList instructionList = new InstructionList(); ConstantPoolGen constantPoolGen = classGen.getConstantPool(); String className = classGen.getClassName(); for (Method method :clazz.getMethods()) { MethodGen wrapGen = new MethodGen(method, className, constantPoolGen); if (method.getName().contains( "executeQuery" )){ // 메소드명이 executeQuery 인경우에만 아래 실행 instructionList.append(instructionFactory.createFieldAccess( "java.lang.System" , "out" , new ObjectType( "java.io.PrintStream" ), Constants.GETSTATIC)); instructionList.append(instructionFactory.createLoad(Type.OBJECT, 0 )); instructionList.append(instructionFactory.createInvoke( "java.io.PrintStream" , "println" , Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL)); // 위 세줄이 첫번째 아규먼트를 불러와서 system.out 출력하게된다. instructionList.append(wrapGen.getInstructionList()); // 윈래 인스트럭션 리스트를 불러와서 붙임 wrapGen.setInstructionList(instructionList); //원래 메소드젠에 그 인스트럭션 리스트로 교체 classGen.addMethod(wrapGen.getMethod()); // 메소드젠으로부터 메소드를 받아 클래스젠에 추가 } } return classGen.getJavaClass().getBytes(); // 클래스젠으로부터 클래스를 받아 바이트코드로 리턴 } catch (Exception e) { throw new IllegalClassFormatException(e.getMessage()); } } } } |
즉, 위에서 사용한 방식을 요약하면
클래스 파싱->메소드 검사후 조건검색->새 메소드인스트럭션 생성->인스트럭션에 아규먼트출력절차 추가->원본 인스트럭션 추가->생성된 인스트럭션을 원본 메소드에 교체->메소드를 클래스젠에 추가->클래스젠으로부터 클래스를 받아 바이트코드로 출력 하게된다.
이를 지칭하여
BCEL on fly, dynamic BCEL 이라고 부르기도 한다.
'Study > BCI&BCEL&ASM' 카테고리의 다른 글
BCEL 중요용어 및 사용법 간단 소개. (0) | 2015.01.12 |
---|---|
bcel 6 다운로드 (0) | 2015.01.12 |
BCI 기술과 BCEL (0) | 2015.01.12 |
ASM 으로 바이트코드 변경하기. (0) | 2014.08.21 |
BCEL 그나마 나은 참고자료.. (0) | 2014.08.04 |
BCEL 중요용어 및 사용법 간단 소개.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Field // 변수와 비슷한, 저장공간 등의 개념으로 볼 수 있다. LocalVariable // 메소드의 지역변수를 나타내며, 메소드 프레임 내의 스코프, 이름, 시그네쳐, 인덱스를 포함한다. Attribute // 상수값, 소스파일, 코드, Exceptiontable, LineNumberTable, LocalVariableTable, InnerClasses 등이 있음. ConstantPool // 클래스나 인터페이스의 이름, 필드명 등의 클래스파일 구조와 서브구조로부터 참조가능한 모든 상수를 담고있는 구조. Instruction(~= Byte Code) // 자바 바이트코드로 되어있는 절차. InstructionFactory // 컴파일러의 뒷단에서 바이트코드를 생성하는 것 이라고 되어있으며 Instruction을 생성하는 역할을 한다. InstructionList.append(InstructionFactory.createStore(Type.OBJECT, 7 /* slotnumber */)); // 변수저장 InstructionList.append(InstructionFactory.createLoad(Type.OBJECT,7 /* slotnumber */ )); // 변수로드 InstructionList.append(_factory.createReturn(Type.VOID)); // 리턴타입지정 InstructionList.append(_factory.createInvoke( "two" , "asdf" , Type.VOID, new Type[] { Type.INT, Type.INT }, Constants.INVOKESTATIC)); // 메소드 호출 InstructionList.append(_factory.createFieldAccess( "java.lang.System" , "out" , new ObjectType( "java.io.PrintStream" ), Constants.GETSTATIC)); // 필드엑세스 생성 MethodGen // 메소드를 만드는 템플릿 정도로 생각하면되며, `getMethod()'를 통해 method를 반환한다. MethodGen method = new MethodGen(ACC_PUBLIC | ACC_STATIC, Type.VOID, new Type[] { Type.INT, Type.INT }, new String[] { "arg0" , "arg1" }, "asdf" , "two" , il, _cp); // 액세스 플래그, 타입, 매개변수타입(혹은 타입배열), 매개변수명, 메소드명, 클래스명, instructionlist, constantpoolgen |
'Study > BCI&BCEL&ASM' 카테고리의 다른 글
BCEL로 런타임에 Arguments(매개변수들) 캡쳐하는 Instruction 추가하기. (0) | 2015.01.12 |
---|---|
bcel 6 다운로드 (0) | 2015.01.12 |
BCI 기술과 BCEL (0) | 2015.01.12 |
ASM 으로 바이트코드 변경하기. (0) | 2014.08.21 |
BCEL 그나마 나은 참고자료.. (0) | 2014.08.04 |
bcel 6 다운로드
최신 개발버전 스냅샷 위치는 다음과같음
http://repository.apache.org/content/groups/snapshots/org/apache/bcel/bcel/6.0-SNAPSHOT/
'Study > BCI&BCEL&ASM' 카테고리의 다른 글
BCEL로 런타임에 Arguments(매개변수들) 캡쳐하는 Instruction 추가하기. (0) | 2015.01.12 |
---|---|
BCEL 중요용어 및 사용법 간단 소개. (0) | 2015.01.12 |
BCI 기술과 BCEL (0) | 2015.01.12 |
ASM 으로 바이트코드 변경하기. (0) | 2014.08.21 |
BCEL 그나마 나은 참고자료.. (0) | 2014.08.04 |
BCI 기술과 BCEL
BCI(Byte Code Instrumentation) 란 *.class 와 같이 바이트코드로 컴파일된 자바 클래스파일을 수정하고 다루는 도구 혹은 기술이다.
자바는 javac 로 컴파일할때 완전한 기계어 코드가 아닌 소스와의 중간형태인 Byte Code 로 생성하게되는데 이게 도움을 받으면 어느정도 읽고 수정할수 있다.
자바에서도 클래스파일을 다루는 법을 제공하며 응용 라이브러리로는
Apache Commons 의 BCEL ( 정식 5.2, 개발버전 6.0)
Object Web 의 ASM
JBOSS 의 JavaAssist
등이 있고 본 블로그에서는 APACHE의 BCEL을 주로 다룰 예정이다.
필자가 다루고있는 라이브러리는 BCEL (Byte Code Engineering Library) 인데 이름만큼이나 정석(?)의 사용법을 제공한다.
바이트코드를 수정하고 작업을 추가하는 등의 작업을 바이트코드엔지니어링 이라고 하는데 앞으로는 작업 이라고 줄여서 말하겠다.
추측하건데 BCEL설계할당시에는 이미 컴파일된 *.class 파일작업만을 염두해두고 만들어졌을것이라고 생각한다.
라이브러리 측면에서 볼때 실시간으로 혹은 임시적으로 클래스파일을 작업하는 부분에대해선 제공되는 사항이 없는데,
이는 ASM 과 Javaassist 도 마찬가지.
그런데 자바1.5 부터 transform 등의 방법으로 런타임 작업이 가능해졌다.
class 파일을 작업하여 영구적으로 저장하는것을 영구작업,
런타임에 작업하여 임시적으로 사용하는것을 임시작업 이라고 하고 이야기를 진행하겠다.
왜 굳이 BCI 기술을 이용해서 코드에 새로운 작업을 추가하는가?
BCI 기술에대해 처음 접하는 대부분의 사람들이 위와같은 질문을 한다.
"왜 굳이 어려운기술을 써가며 작업을 추가하느냐", "소스를 추가하면 되는거 아니냐" 등의 많은 질문을 받는데, 반대로 질문을 던져보면 답은 쉽게 나온다.
만약에 소스가 없거나, 라이센스문제등으로 소스추가나 수정이 불가능한경우라면 어떻게 할 것인가 하는 질문이다.
BCI의 이점은 여기에있다. 소스가 필요없다. 어떤 라이브러리를 사용하건 타겟클래스와 메소드나 변수정보 등만 파악할 수 있으면 작업이 가능하다.
또한 반복적인 작업을 줄일수도 있다.
작업하는 메소드 를 만들어놓고 특징이 비슷한 여러개의 메소드에 적용 할 수도 있다.
그러나 가장 큰 메리트는 역시 '소스수정을 직접 하지 않아도 추가절차를 진행할 수 있다.' 는 점이다.
소스를 추가하는게 부담스럽지않은 소규모의 웹 어플리케이션에선 큰 차이가 없지만, 만약 컴파일된 코드만 수십 수백개가 되는 대형 어플리케이션이라면
이야기는 달라진다. 서비스를 제공하기위해 타겟 혹은 고객이 추가적으로 수고해야하는 일을 없앨 수 있다.
'Study > BCI&BCEL&ASM' 카테고리의 다른 글
BCEL 중요용어 및 사용법 간단 소개. (0) | 2015.01.12 |
---|---|
bcel 6 다운로드 (0) | 2015.01.12 |
ASM 으로 바이트코드 변경하기. (0) | 2014.08.21 |
BCEL 그나마 나은 참고자료.. (0) | 2014.08.04 |
bcel api doc (0) | 2014.08.01 |
ASM 으로 바이트코드 변경하기.
ASM 에 대한 정리가 잘 되어있다.
'Study > BCI&BCEL&ASM' 카테고리의 다른 글
bcel 6 다운로드 (0) | 2015.01.12 |
---|---|
BCI 기술과 BCEL (0) | 2015.01.12 |
BCEL 그나마 나은 참고자료.. (0) | 2014.08.04 |
bcel api doc (0) | 2014.08.01 |
BCEL 메소드 추가하기 (0) | 2014.08.01 |