이번 아티클에서는 클래스패스에 포함되어있지 않은 클래스에 대한 동적변경시 애플리케이션의
재기동없이 클래스를 로딩하는 방법을 보고자 한다.
예전에 이러한 것을 아티클로 쓰겠다고 공언했으나, 금방 지키지 못한걸 후회하고 있다.
왜냐~ 놀새~가 너무나도 바뿌기 때문에.. 바쁜게 좋긴 하지만 너무 바빠도 죽을 것 같다.
게다가 팔목까지 부러진(5월1일 노동절 기념으로 부러짐) 마당에 손까지 제대로 쓸수 없던 마당에
일까지 많이 겹쳐버리니 원~
▶ Dynamic Class Loading Technique
이번에 여러분들에게 보일 예제에서는 애플리케이션의 재시작없이 동적으로 변경된 클래스파일을
알아채어 클래스로더에 등록시켜 사용할 수 있는 deploy전략을 세워보도록 한다.
시간이 있으면 구체적인 것을 이용하여 실제 사용될 수 있는 것을 보이겠지만 항상 그랬듯이
우선 맛보기로 기본적인 기초를 다지도록 하자.
여기서 적용되는 것은 분리된 클래스 로더를 이용하여 daemon 과는 관계없이 우리의 애플리케이션에만
적용이 되도록 하게 해야 할 것이다.
자. 이 글을 읽는 당신 먼저 생각부터 해보라~
어떻게 하면 클래스가 변경되었다는 것을 알아차리게 할 것인가?
스레드로 무한 루프 하나 돌리면서 무조건 클래스롤 reload하게 할것인가? 아니면 클래스가
변경되었을 때만 로딩하게 할것인가?
잠시 딴 이야기지만
매트릭스2에서 오라클은 "선택은 이미 되었다. 문제는 당신이 그것을 이해해야 하는것이다~"라고
이야기한다. 위에서 질문을 한것처럼 결정은 이미 된 상황이며, 놀새~는 그것을 어떻게 여러분에게
이해를 시켜야 하며, 여러분들은 어떻게 이해할것인지의 문제인지도 모르겠다.
이미 선택은 했을 것이리라 본다. 후자쪽이 더 가까울듯 한데 만약에 후자를 선택했다면
그 방법은? 당근 API에 나와있으리라~
여기서 중요한것은 클래스가 변경되어 새로운 클래스로더를 사용했을 때는 기존의 클래스로더를 불필요하게
남겨두지 말고 없애버려야 함은 물론이다.
또한 이러한 정보들이 명시적인 클래스패스에 추가가 되어있지 않음을 알아야 할 것이다.
우선 변경대상이 되는 클래스를 먼저 작성해보도록 하자.
▶ ReloadTargetClass.java
package carouser;
public class ReloadTargetClass{
public String hiCarouser(){
return "기초에 충실하자";
}
}
|
위와 같은 클래스가 작성되었다고 했을 때 일반 java.io.File API를 통하여 위 파일이 변경된것을
알아차리게 한다면 어떻게 코드가 작성이 되어야 할것인가?
lastModified()?
흠.. 위의 API를 쓰게 되면 파일의 최종 변경시간을 알 수 있을 것이므로, 해당 메소드를 이용하여
파일이 변경되게 하는 것을 알면 될것이다.
어떠한 것들이 필요할 것인지 분석을 해보도록 하자. 시나리오를 짜자는 것이다.
놀새~가 생각하는 것과 비슷하게 여러분도 생각하리라~
- 음~ 클래스로더가 필요하고, 동적으로 변경이 된다 하였으니 우선 파일이 변경된 것을 알아야한다.
- 파일이 변경된 것을 체크할 수 있는 일종의 Observer thread가 필요할 것이다.
- 변경이 되었다면 lastModified를 체크하여 기존의 값과 다르다면 클래스로더를 생성하여
해당 클래스를 로딩하여 서비스하게 하면 될것이다.
위의 가정에서 필요한 것들을 가장 단순하게 적어보았다.
더 필요하다면 여러분들이 놀새~가 작성한 코드를 수정하여 테스트해 보면 될터이고..
자~ 위의 내용을 이용하여 코딩을 하도록 한다.
▶ ReloadTargetClass.java
import java.net.*;
import java.io.*;
import java.util.*;
import carouser.*;
public class ClassObserver extends Thread{
static String className = "carouser.ReloadTargetClass";
long lastModified = 0;
ClassLoader cl = null;
public void observe() {
URL[] urls = null;
try {
// 파일객체를 URL로 돌린다.
File dir = new File(System.getProperty("user.dir")
+File.separator+"carouser"+File.separator);
System.out.println(dir);
URL url = dir.toURL();
urls = new URL[]{url};
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
// URL로 생성된 디렉토리에 새로운 클래스로더를 생성하도록 한다.
ClassLoader cl = new URLClassLoader(urls);
// 재기동할 클래스를 처리한다.
Class cls = cl.loadClass(className);
// 해당 클래스에 대한 인스턴스를 생성하도록 한다.
ReloadTargetClass obj = (ReloadTargetClass)cls.newInstance();
System.out.println("Object : " + obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException e2) {
e2.printStackTrace();
}
}
public void run() {
while(true) {
File target = new File(System.getProperty("user.dir")
+File.separator+"carouser"+File.separator + "ReloadTargetClass.class");
if( lastModified != target.lastModified() ) {
lastModified = target.lastModified();
System.out.println("Class Modified : " + new Date(lastModified));
observe();
}
try{
Thread.sleep(10 * 1000); // 10초마다 한번씩 로딩해본다.
}catch(Exception e){}
}
}
public static void main(String [] args) {
new ClassObserver().start();
}
}
|
코드가 잘 보이는가? 스레드를 이용하여 파일이 변경되는 것을 체크하도록 하였다.
또한 파일의 변경시간을 lastModified()메소드를 이용하여 해당 클래스가 다시
URLClassLoader를 이용하여 동적으로 로딩하도록 하게 한다.
대략 10분정도 기존코드에 첨가하여 만든 코딩이므로, 조금 문제가 있기도 하겠지만
놀새~왈 : "그런 걸루 딴지 걸지 마세요~!"
왜냐~ 그냥 여러분들이 고쳐쓰면 그만이므로.. ^^
간단하지만 오묘하다. 코드를 음미해보기 바라며, 응용해서 좋은 클래스 로더를 만들수 있어야 한다.
엔진들에 들어가는 클래스로더들은 정교하게 만들어져 있다.
▶ Conclusion
클래스로더에 대하여 아주아주 간단하게 살펴보았다. 나머지는 여러분들이 응용하기 나름이다.
참고로 다시한번 위의 소스는 classpath옵션이 사용되지 않는다는 것을 알아야 하며,
JSP/Servlet엔진을 사용해보았다면 WEB-INF가 왜 규칙에 의하여 그렇게 만들어져 있는지를
알아야 한다.
궁금하면 엔진들의 property를 찍어보면 클래스패스를 잡기위한 경로로 여러분들이 엔진을
설치한 곳의 ${document.root}/WEB-INF/classes가 잡혀있음을 반드시 반드시 알아야 한다.
끝~!! 아싸~!
|