스프링 DM 프로젝트 빌드하기

메이븐 기반 프로젝트기 때문에, 간단하게 mvn package로 빌드 할 수 있을 거라고 생각했는데;;; 땡.. 틀렸습니다. mvn packae로 빌드하면 다음과 같은 에러를 볼 수 있습니다.

[ERROR]

Mojo:

    org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile

FAILED for project:

    org.springframework.osgi:spring-osgi-mock:bundle:1.2.0-m1

Reason:

C:\java\spring-osgi-1.2.0-m1\src\mock\src\main\java\org\springframework\osgi\mock\MockServiceReference.java:[23,25] package org.osgi.framework does not exist

OSGi 플랫폼이 없어서 발생하는 에러로, 스프링 DM 프로젝트를 빌드 할 때 프로파일을 선택해줘야 한다는 군요. 그래서 스프링 DM 프로젝트에 있는 프로파일을 살펴봤습니다.

사용자 삽입 이미지
여러가지가 있었습니다. 맨 위에 세 개는 OSGi 플랫폼이고, jdk 버전을 1.5 이상용도로 빌드해서 애노테이션 지원 기능을 사용할 수 있게 빌드 할 수 있나보네요. it은 통합 테스트(이 옵션을 안 주면 단위테스트만 합니다.)

mvn -Pequinox

통합 테스트까지 하면서 빌드 하려면

mvn -Pequinox,it

굳이 빌드를 안 해도 dist 폴더에 들어있긴 하지만… 오픈 소스쓰는 기본 자세라는 사부님 말씀. 캬~ 덕분에 오늘도 한 수 배웠습니다.

메이븐 프로파일은 안 써봤는데, 저걸 사용해서 빌드 하면 여러 환경에 따른 빌드 커스터마이징이 가능하군요. 특히 저 통합테스트가 눈에 띄는데 빌드 시간을 엄청 오래 잡아먹는 통합 테스트들은 주기 적으로만 실행하고 한 번의 커밋당 실행하는 빌드는 단위테스트만 실행하게 할 수도 있겠습니다.. 흠.. 프로파일 좋구나.

OSGi에서 SessionFactory(Hibenate) 사용하기

참조 :
http://www.osgi.org/blog/2007/06/osgi-and-hibernate.html
http://notehive.com/wp/2008/07/23/osgi-hibernate-spring-dm-sample/

번들 세 개만 살펴보겠습니다.

1. hibernate-class
2. hibernate-session
3. model-a

1. hibernate-class

이 녀석은 하이버네이트 라이브러리를 묶어놓은 번들입니다. 얘가 담고 있는 라이브러리는 다음과 같습니다.
사용자 삽입 이미지
이렇게 묶어놓은거 말고 스프링 번들 저장소에서 다운로드해서 일일히 설치해도 되야 할 것 같은데, 저번에도 해봤고, 오늘도 다시 시도 해봤는데, 똑같은 문제가 발생하고 있어서 실패했습니다. 아무래도 스프링 DM 지라에 올리던가 포럼에 올려서 물어봐야 할 것 같네요.

2. hibernate-session

이 녀석이 하는 일은 많습니다. 테스트 DB를 만들고, 하이버네이트 애노테이션을 사용한 도메인 클래스들도 있고, SessionFactory도 만듭니다. 단, SessionFactory를 만들 때 좀 특이하게 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 클래스를 사용하여 생성합니다. 실질적으로 SessionFactory를 만들어 내는 클래스입니다. 이 클래스의 핵심 메소드인 createNewSessionFactory() 메소드에서 JDK Proxy 클래스를 사용해서 프록시 객체를 생성하고 있습니다.

OSGi 서비스로 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration를 공개하고 있습니다.

3. model-a

이 번들에는 하이버네이트 애노테이션을 사용한 모델과, DAO 구현체가 들어있습니다. SessionFactory와 TransactionManager를 내부에서 빈으로 정의하고 있는데, SessionFactory를 만들 때 위에서 살펴본 hibernate-session 번들이 공개한 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 서비스를 사용합니다. SessionFactory를 빈 설정만 보면, hibernate-session이나 model-a나 동일합니다.

단, 이 빈은 com.notehive.osgi.hibernate_samples.session.DynamicConfiguration 서비스를 가져옵니다. 그리고 DAO에서는 해당 서비스를 사용해서 만든 SessionFactory를 사용하죠.

그리고 DynamicConfigurationListener 라는 빈을 등록해서 DynamicConfiguration 이 객체에다가 하이버네이트 애노테이션이 붙어있는 클래스를 추가하거나, 제거하는 코드를 넣어뒀습니다.

마지막으로 주목할 것은 osgi.bnd 파일의 설정인데..

Hibernate-Contribution: default; \
    classes=”com.notehive.osgi.hibernate_samples.model.a.A1″

이런식으로 속성/값을 추가해뒀고, 이 값을 hibernate-session 번들의 BundleTracker가 읽고 DynamicConfiguration에 애노테이션이 붙은 클래스를 추가/제거 합니다. 그 뒤엔 당근 새로운 SessionFactory를 만들어 가지게 됩니다.

캬…이제야 OSAF를 돌릴 수 있는(정확하겐 하이버네이트 App를 돌릴 수 있는) 플랫폼을 구성하는 실마리를 찾은 것 같습니다.

OSGi 패키지가 아니라 서비스야 말로 진정한 Dynamic

번들과 번들 사이에서 자신이 가지고 있는 정보를 공유하는 방법은 두 가지 입니다. 패키지 안에 있는 클래스들을 공개해서 상대방이 내가 가진 클래스의 객체를 만들어서 사용하게 할 것이냐, 아니면 내가 패키지 말고 내가 객체를 만들어서 제공할 것이냐. 후자가 바로 서비스. 전자는 패키지입니다.

차이는 매우 큽니다. Dynamic Module System에서 Dynamic 이라고 할려면 사실 상 패키지 공개로는 아무 의미가 없고,  Service-Export/Import를 해야 의미가 있습니다. 왜냐면 말이죠… 생각을 해보면 됩니다.

A 번들 whiteship 패키지에 Whiteship.java 클래스가 있고,
B 번들 blueship 패키지에 Blueship.java 클래스가 있다고 하겠습니다.

이 때 B 번들에서 A 번들에 있는 Whiteship 타입의 객체가 필요합니다. 그래서 A 번들에서 Export-Package로 whiteship 패키지를 등록하고, B 번들에서는 Import-Package로 whieship을 등록했습니다. 그런 다음 B 번들에서 Whiteship 타입 객체를 만들어서 사용합니다.

자 A 번들의 Whiteship.java 클래스가 바꼈습니다. 어떡할건가요? A 번들을 다시 설치합니다. B 번들에서 사용하고 있는 Whiteship 타입 객체는요?? 그대로죠. 뭐 변한게 없습니다.  뭐가 동적으로 바뀌죠? 바뀌는거 없죠? 이제 뭔가요? 이제 Dynamic 인가요? 아니죠.

패키지 대신 서비스로 단어를 바꿔서 다시 생각해보시면 뭔가가 달라집니다.

다시, (서비스를 사용한다는 가정하에) A 번들의 Whiteship.java 클래스가 바꼈습니다. A 번들을 다시 설치해야겠군요. 이건 당연한 겁니다. 설마 A 번들도 다시 설치하는데 이게 뭐가 동적이야??? 라고 생각하시는 분은 안 계시죠? 만약에 그러면 그건 좀 코메디입니다. ㅋㅋ 아무튼 잡담이었구요. 자. 이 다음엔 어떤 일이 벌어질까요? B 번들이 사용하면 Whiteship.java 타입의 서비스까지 바뀝니다. 캬~~ 놀랍죠? 어떻게 바뀌냐구요? A 번들이 죽을 때 자기가 등록한 서비스들도 전부 죽입니다. 그럼 B 번들이 사용하고 있던 Whiteship 타입의 서비스도 죽었겠죠. 그런 다음 A 번들이 다시 살아나면, 서비스를 등록하고 그럼 그 서비스를 기다리고 있던 B 번들이 다시 Whiteship 타입의 서비스를 사용하는 겁니다.

복잡하죠? 네.. 사실 이렇게 해피한 시나리오대로 흘러가지 않을 가능성도 많습니다. 설정하기 나름입니다. 대기 시간 설정이라덩가. 서비스의 필수 여부 설정이라덩가. 덩덩덩.

그리고 또 있습니다.

패키지로 export/import  할 때는 impl 까지도 export 해줘야겠죠? 객체를 생성하려면 어차피 구현한 클래스까지 알아야 할테니까요. 그런데 서비스로 공개할 떄는 구현체까진 안 알려줘도 됩니다. 구현은 감추고 인터페이스만 공개할 수 있는거죠. 캬~

Anyway!! 패키지를 사용하는거 보다는 서비스를 사용하는게 진정한 Dynamic 이라는거 아시겠죠?

그럼? 서비스만 쓰지 패키지는 왜 있는거야?? 라는 생각.. 드시죠??

저도 좀 생각을 해봤는데요. GenericDao 같은 클래스의 서비스가 필요한가요? 그냥 상속해서 쓰면 그만이죠? 즉 이렇게 패키지로 공개할 것인가 서비스로 공개할 것이냐는 용도에 따라 좀 달라질 것 같습니다. 클래스가 필요하면 패키지로, 객체가 필요하면 서비스로. 글쵸? 그런거 같죠? ㅎㅎ; 저도 잘 몰라요.

흠… Hibernate Annotation 번들 왜이래 왜 ㅠ.ㅠ

osgi> diag 13
initial@reference:file:com.springsource.org.hibernate.annotations-3.3.1.ga.jar/ [13]
  Constraints from the fragment conflict with the host: Import-Package: org.apache.commons.logging; version=”[1.1.1,2.0.0)”
  Missing Constraint: Fragment-Host: com.springsource.org.hibernate; bundle-version=”[3.2.6.ga,3.3.0)”

osgi>

흠… 이상하네. host(org.hibernate) 랑 dep 충돌이 난다는데, host도 commons.logging 1.1.1 사용하고, 이 녀석도 1.1.1 참조하는데, 뭐가 충돌이 난다는거얌 ㅠ.ㅠ

왜그러니.. 내가 널 만들어줄까??

pom.xml에서 bnd 설정 파일 분리하기

사용자 삽입 이미지

bnd 설정 파일을 pom.xml에서 분리하고 pom.xml의 bnd 플러긴 설정은 다음과 같이 수정합니다.

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <version>1.4.0</version>
                <configuration>
                    <instructions>
                        <_include>-osgi.bnd</_include>
                    </instructions>
                </configuration>
            </plugin>       

pom.xml은 훨씬 가벼워지고, bnd 설정과 pom.xml을 유지보수 할 때도 이렇게 나눠 두는게 좋을 듯 합니다.