JAVA Hot Deploy 도구 Jrebel ( 舊 JavaRebel )

왜 Java Enterprise Web Application에서 hot deploy가 어려우며 문제가 되는지는 아래 아티클 참조.

Tomcat , Weblogic Application Server, WebSphere, JBoss 같은 서블릿컨테이너나 J2EE컨테이너도
옵션을 통해 Hot Deploy를 지원하긴 하지만 어디까지나 Context 전체를 리로딩하는 수준이다.

그런 식으로나마 서버 Shutdown , Startup 조작을 줄여주는 게 고맙기는 하지만 클래스 Hot Deploy는 자바
웹어플리케이션 개발자에게는 성가신 문제일  수밖에 없다.

이런 문제를 풀기 위해 몇몇 솔루션이 있는데 최근에 사용해본 게 아래의 솔루션이다.
JRebel (舊 JavaRebel)

Jrebel

JRebel의 리디플로이 가능 범위


JRebel는 훌륭한 도구이긴 하지만, 위 링크에 설명된 몇 가지 이유로 완전한 Hot Deploy를 제공하는 것은 아니다.
다만, 분명한 것은 개발 시 로컬 서버나, 테스트 서버의 shutdown 횟수를 줄여줄 뿐만 아니라 클래스 리로딩
시간을 현격히 줄여주는 것으로도 JRebel을 사용할 충분한 가치가 있다고 생각한다.
 
덧. 위에 소개한 JRebel은 Open Source나 Freeware가 아님.
     단, Open Source Software개발자와 Scala 개발자에 한해 무료로 제공하고 있음.
2009/10/12 13:15 2009/10/12 13:15
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다

컴포넌트 시스템과 클래스 로더 경계 :: Java Class Loader

어느 곳에서나 소프트웨어 개발자들은 단일 애플리케이션 개발 전략을 넘어서서 상호작용(interoperating) 애플리케이션 시스템 개발로 나아가고 있다. 현재 엔터프라이즈 개발자들이 당면하고 있는 문제들 중 하나는 최근의 WAR 와 EAR 파일 등 Web and Enterprise Archives 같은 단일 애플리케이션 디플로이먼트 구조에서 느슨하게 결합된 애플리케이션 컴포넌트 시스템으로 어떻게 옮겨갈 것인가 이다. 개발자들은 J2EE 플랫폼의 기본 기능과 호환됨과 동시에 코드 재사용을 늘리고 애플리케이션 복잡성을 줄이기 위해 컴포넌트 기반 애플리케이션 전략을 사용하길 원한다. JARs, WARs, EARs 등과 같이 플러그할 수 있는 컴포넌트 아카이브를 개발하는 것이 이 전략의 관건이 될 것이다.

그러나 J2EE에서의 컴포넌트 기반 애플리케이션 시스템 개발과 배포를 둘러싼 많은 함정이 도사리고 있다. 중요 함정 중 한가지는 Java 버추얼 머신에서 클래스 로더의 경계를 교차하여 기능하기 위한 컴포넌트 시스템을 얻는데 따르는 어려움이다.

클래스 로더는 환경 구획의 의미로 생각하면 된다. 명심해야 할 것은 클래스 로더는 하위 클래스 로더들을 생성할 수 있으며, 따라서 하위환경을 만들어낼 수 있다는 것이다. 이 하위 클래스 로더 환경의 클래스들은 윗 계급인 시스템 클래스 로더쪽을 볼 수 있지만 더 하위 계급의 클래스들은 볼 수 없다.

이런 종류의 다단계적 환경 구조는J2EE 서버 환경에서 매우 보편적이며 심지어 J2EE 기술을 둘러싼 다양한 규격에는 권장되고 있는 사항이다. 예를 들어 서블렛들은 각각 WAR 파일에 패키지화되어 있으며, 오버롤 시스템의 각각의 클래스 로더 컨텍스트에 디폴트값으로 주어져있다. 이는 WEB-INF/lib 디렉토리에 포함되어 있는 JAR 파일은 다른 웹 아카이브의 JAR 파일 안에 저장되어 있는 클래스들에 접근하지 못한다는 것을 의미한다. 단일 애플리케이션에 패키징하기 위해 WAR을 사용할 때 이 배열은 허용가능하나 올인원 전략을 넘어서서 좀 더 컴포넌트 기반 애플리케이션 아키텍처로 나아간다면 바로 문제가 되어버릴 것이다.

이런 문제들의 증상은 확실하지 않을 때가 많으므로, 클래스 로더 경계를 교차하여 컴포넌트를 전달하지 못한다는 것을 진단하기는 매우 어렵다. 예를 들어 웹 애플리케이션 아카이브의 서블렛들 사이로 전달시키고자 하는 Foo라는 이름의 오브젝트가 있다고 하자(어느 한 웹 아카이브 안의 서블렛에서부터 다른 웹 파카이브의 서블렛으로 전달됨을 의미). Foo컴포넌트를 위한 클래스 파일은 JAR 파일에 패키징되어 있으며 JAR 파일의 동일한 복사본이 각 웹 아카이브의 /WEB-INF/lib 디렉토리 안에 있다. 다음의 코드를 작성하자.

   /* This code runs in a servlet in WAR #1 */
   SystemScopeObjectCache cache = 
       SystemScopeObjectCacheFactory.getInstance();

   WARScopeFoo foo = new WARScopeFoo ();
   System.out.println(foo);
   cache.addToCache("myFooObject", foo);

   /* This code runs in a servlet in WAR #2 */
   SystemScopeObjectCache cache = 
       SystemScopeObjectCacheFactory.getInstance();
   Object o = cache.getFromCache("myFooObject");

   try {
     // the following throws a ClassCastException! 
     WARScopeFoo foo = (WARScopeFoo)o; 
        
   } catch(ClassCastException e) {
     e.printStackTrace();
   }

각 웹 아카이브의 서블렛에 접근가능한 WARScopeFoo 오브젝트를 위한 컴포넌트 캐시를 쉽게 생성할 수 있다. 그러나 만약 서블렛 A가 오브젝트의 인스턴스를 중앙 캐시로 전달하고, 다른 WAR의 서블렛 B가 캐시로부터 인스턴스를 꺼내 Object에서 WARScopeFoo로 전달하려한다면 시스템은 ClassCastException을 전달하게 될 것이다.

이 상황은 클래스 로더를 고려한 것이 아니라면 아무 의미가 없다. 서블렛 A 에서 참조하는 WARScopeFoo 클래스는 서블렛 B에서 참조하는 WARScopeFoo클래스와 전혀 연관 없는 다른 클래스 로더로부터 생성된다. 정확히 얘기하자면, 그 둘은 의도적으로 전혀 연관되어 있지 않다. 이것은 같은 서블렛 컨테이너에서 구동되는 웹 애플리케이션 간의 네임스페이스 보전성을 강화하기 위한 안전한 매커니즘이다.

클래스 로더가 충돌한다는 또 다른 신호는 시스템의 단일 클래스에서 다중 인스턴스를 찾았을 때이다. (단일 클래스에는 단 한 개의 클래스 인스턴스가 생성되어야 한다.) 기술적으로 단일 인스턴스는 그 클래스 로더 안에서만 독자적일 뿐이다. 따라서 클래스 로더 단계에서 단일성에 의지하는 것은 위험하다. 다음의 예제를 보자.

   /* MyServlet in WAR #1 */
   WARScopeSingleton cache = WARScopeSingleton.getInstance();
   WARScopeFoo foo = new WARScopeFoo ();
   cache.add("myFooObject", foo);
   System.out.println(cache.length()); //output is 1

   /* MyOtherServlet in WAR #2 */
   WARScopeSingleton cache = WARScopeSingleton.getInstance();
   System.out.println(cache.length()); //output is 0!

이 예제에서 코드는 단일 클래스에서의 오브젝트들을 저장한다. 그러나 단일 클래스에서의 스코프는 WAR 클래스 로더를 필연적으로 발생시킨다.

다행히 엔터프라이즈 Java 개발자들을 위해 이런 장애들을 극복할 몇 가지 방법이 있다. 첫번째 단계는 어떤 클래스 로더 경계가 늘 존재하는 것이 보장되는지 확인하고 그에 대한 전략을 계획하는 것이다. 모든 J2EE 환경에는 세 개의 클래스 로더 레벨이 구축되어 있다. 시스템 단계의 컨텍스트는 대부분 VM을 교차하며 J2SE와 J2EE 플랫폼의 클래스와 호환된다. 이것은 애플리케이션 서버가 자체적으로 구동하는 레이어이다.

다음 단계는 Enterprise Archive 컨텍스트인데, 이는 엔터프라이즈 애플리케이션의 모든 JAR와 WAR를 포함하고 있다.

마지막 단계는 Web Archive 컨텍스트이며, 이는 WAR 파일 /WEB-INF/classes디렉토리의 모든 파일과 /WEB-INF/lib 디렉토리의 모든 JAR 파일을 포함하고 있다. WAR 파일 안에 로딩된 모든 클래스가 상호 접근 가능함은 물론이고 EAR 파일과 System 클래스 로더의 클래스들과도 접근가능하지만 다른 WAR 파일 안에 로딩된 클래스들과는 접근될 수 없다.

따라서 웹 아카이브 간의 사용자 비즈니스 오브젝트를 공유하고자 한다면 오브젝트의 JAR 파일을 EAR 클래스 로더 컨텍스트에 위치시키고 WAR 파일의 /WEB-INF/lib 디렉토리는 피하는 게 좋다. 다음은 그 예이다.

   /* MyServlet in WAR #1 */
   EARScopeCache cache =EARScopeCache.getInstance();
   EARScopeFoo foo = new EARScopeFoo();
   cache.add("MyFooObject", foo);

   /* MyServlet in WAR #2 */
   EARScopeCache cache = EARScopeCache.getInstance();
   Object o = cache.get("myFooObject");
   EARScopeFoo foo = (EARScopeFoo)o; //SUCCESS!

이 작업의 이유는 각각의 서블렛은 상위 계급의 클래스 로더를 추구하고 WAR 클래스 로더 컨텍스트에 클래스들을 로딩하는 대신 같은 EARScopeCacheEARScopeFoo오브젝트를 찾는다는 데에 있다.

그러나, EAR 파일을 조합하는 것은 서블렛 규격의 참조 구현인 Tomcat 같은 어떤 컨테이너 에서는 옵션이 아니다. Tomcat은 EAR 파일에서 클래스들을 로딩하는 정보처리 기능이 없으며 따라서 다중 웹 아카이브에 클래스들을 교차할 수 있게 만들지 못한다. 그러나 Tomcat은 일반 클래스 로더를 보유하여 /common디렉토리의 모든 JAR 파일을 모든 웹 아카이브 컨텍스트위에 직접적으로 존재하여 접근가능한 클래스 로더 스페이스에 로딩한다.

생각해볼 수 있는 다른 기술로는 서로 다른 컴포넌트 하위 시스템 간에 데이터를 왕복전달하기 위해Java 배열을 사용하는 것이 있다. 두개의 애플리케이션으로부터의 컴포넌트가 바이트로 배열된 데이터를 저장할 수 있는 공유 장소가 있고, 버전 충돌이 잠재적으로 있다는 사실을 배제하면 이 전략은 빠르고 효과적이다.

원문 출처 : http://kr.sun.com/developers/techtips/e2004_0824.html#2(새 창으로 열기)

2007/07/22 13:39 2007/07/22 13:39
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다