Whiteship's Note

'Spring DM/exercise'에 해당되는 글 30건

  1. 2008.10.16 스프링 DM 프로젝트 빌드하기
  2. 2008.08.25 OSGi에서 SessionFactory(Hibenate) 사용하기
  3. 2008.08.20 OSGi 패키지가 아니라 서비스야 말로 진정한 Dynamic
  4. 2008.08.19 흠... Hibernate Annotation 번들 왜이래 왜 ㅠ.ㅠ
  5. 2008.08.18 pom.xml에서 bnd 설정 파일 분리하기
  6. 2008.08.14 OSGi 기반 프레임워크과 애플리케이션 아키텍처 진화 과정 (2)
  7. 2008.08.14 OSGi에서 Hibernate의 SessionFactory 문제 (2)
  8. 2008.07.31 맥북에서도 Spring DM 웹 번들 설치 성공
  9. 2008.07.20 Late Binding in Java (2)
  10. 2008.07.13 bnd에 번들 실행환경 설정하기
  11. 2008.07.13 OSGi에서 클래스 로딩 순서
  12. 2008.07.12 버전 매기기와 범위 설정
  13. 2008.07.11 OSGi 시작은 상호 참조 제거부터 (2)
  14. 2008.07.10 Required-Bundle을 비추하는 이유
  15. 2008.07.10 bnd 사용해서 API 가져오기(Import)
  16. 2008.07.10 bnd 사용해서 API 공개(Export) 하기
  17. 2008.07.03 BundleManager 0.2 번들 소스 코드
  18. 2008.07.03 BundleManager 0.1 번들 소스 코드
  19. 2008.07.03 HelloBundle 번들 소스 코드
  20. 2008.06.25 번들 라이프사이클 (2)
  21. 2008.06.23 bnd를 소개합니다. (2)
  22. 2008.06.22 Eclipse에서 Felix 사용하기
  23. 2008.04.30 귿!! OSGi위에 웹 애플리케이션 돌리기 성공 with Spring DM (2)
  24. 2008.03.27 Spring Dynamic Modules Demos 실습
  25. 2008.02.06 설맞이 OSGi 간단 예제
  26. 2008.02.06 5. Spring DM을 사용하여 서비스 사용하기.
  27. 2008.02.06 4. Spring DM을 사용하여 서비스 노출하기.
  28. 2008.02.06 3. OSGi 서비스 노출하기와 들여오기.
  29. 2008.02.06 2. OSGi 번들 패키지 노출하기와 들여오기.
  30. 2008.02.06 1. Equinox 기반 OSGi 번들 만들기

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

Spring DM/exercise : 2008.10.16 14:59


메이븐 기반 프로젝트기 때문에, 간단하게 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 폴더에 들어있긴 하지만... 오픈 소스쓰는 기본 자세라는 사부님 말씀. 캬~ 덕분에 오늘도 한 수 배웠습니다.

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


top

Write a comment.


OSGi에서 SessionFactory(Hibenate) 사용하기

Spring DM/exercise : 2008.08.25 14:59


참조 :
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를 돌릴 수 있는) 플랫폼을 구성하는 실마리를 찾은 것 같습니다.

top

Write a comment.


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

Spring DM/exercise : 2008.08.20 22:05


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

차이는 매우 큽니다. 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 같은 클래스의 서비스가 필요한가요? 그냥 상속해서 쓰면 그만이죠? 즉 이렇게 패키지로 공개할 것인가 서비스로 공개할 것이냐는 용도에 따라 좀 달라질 것 같습니다. 클래스가 필요하면 패키지로, 객체가 필요하면 서비스로. 글쵸? 그런거 같죠? ㅎㅎ; 저도 잘 몰라요.

top

Write a comment.


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

Spring DM/exercise : 2008.08.19 12:20


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 참조하는데, 뭐가 충돌이 난다는거얌 ㅠ.ㅠ

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

Write a comment.


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

Spring DM/exercise : 2008.08.18 15:29


사용자 삽입 이미지


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을 유지보수 할 때도 이렇게 나눠 두는게 좋을 듯 합니다.
top

TAG Bnd, maven

Write a comment.


OSGi 기반 프레임워크과 애플리케이션 아키텍처 진화 과정

Spring DM/exercise : 2008.08.14 18:11


대체 어떻게 모듈화 해야 할까...
어떻게 나눌 것인가..
어떻게 구성해야 번들간의 상호참조(CD)를 없앨 수 있을까..
어떻게 나눠놔야 개발을 할 때 여러 번들을 뒤적거리지 않을까..
번들헬이 발생하지 않게 하려면...

위와 같은 고민들은 OSGi와 스프링 DM을 학습하다보면, 자연스레 맞닥드리게 되는 문제들입니다.

이 질문에 대한 답은 모르겠습니다. 사실 답은 있죠. "잘". 그러려면, 많이 실험을 해봐야 합니다. 때마침 저한텐 아주 좋은 실험체가 있습니다. 임상 실험 프로젝트랄까요.ㅎㅎ 스프링 하이버기반으로 세 달에 걸쳐서 만든 시스템이 하나 있습니다. 규모가 크지도 작지도 않고 좋습니다. 도메인 모델이 한 40개 정도되는 프로젝트입니다. 비즈니스 로직도 포함하고 있어서 서로 얽히고 섥혀있지요.

이 프로젝트에서 OSAF15에 들어갈 코드를 분리해냈습니다. 그게 1단계였죠. 분리해낸지는 꽤 됐지만, 주석이랑 테스트 코드를 추가하느라 시간이 좀 지연됐습니다. 어제부로 그 작업도 끝났습니다. 1.5 단계랄까요. 정리하는 단계는 그렇게 끝났습니다.

이제는 본격적으로 2단계로 돌입해서 쪼갠 것을 적용해봐야 합니다. 그래서 검증이 되는거죠. 일단은 OSGi화 하지 않았던 기존 시스템과 동일하게 동작하는것을 목표로 적용합니다.

2단계가 잘 돌아가면, 그 뒤엔 쪼갠 것을 돌리는 상태에서 OSAF 번들만 수정해서 업데이트를 하는 겁니다. 이게 마지막인 3단계입니다.

오늘은 2단계에 막 들어선 날로, 코딩은 별로 못하고 낙서와 그림을 그리고 웹 서핑을 하면서 다른 자료를 찾는데 시간을 많이 보냈습니다. 다행히 어느 정도 성과가 있었습니다.

사용자 삽입 이미지
처음에 그린 그림입니다. OSAF15 번들 자체가 너무 커서, 그 안에 들어있는 몇 개의 패키지를 별도의 모듈로 쪼개는 걸 구상하여 그린 겁니다. OSAF, OSAF-App 이렇게 둘로 쪼개고, 일반 App 번들과 OSAF-App 번들을 WAR 번들에서 참조하는 걸 그리다가.. 문제를 발견했습니다. 그게 바로 아래에 있는 S/F SessionFactory 입니다.

저 때는 아직 문제를 발견했다기 보다.. 뭐랄까.. 냄새가 나고 있었다고 할까요.. 저 땐 단순하게 SessionFactory를 사용한다고만 생각했지 SessionFactory에서 저 번들들 안에 들어있는 모델을 참조해야 한다고.. 즉 상호참조가 발생하리라곤 미쳐 생각을 못하고 있었습니다.

사용자 삽입 이미지
(여러 색의 형광펜을 발견하고, 잘 나오나 확인을 해보는 그림이 좀 멋있어 졌습니다.ㅋㅋㅋㅋ)

두 번째 그림입니다. 첫 번째와 비슷하게 OSAF에서 이번엔 Security 부분을 떼어 내야겠다는 생각이 들었습니다. OSAF-App에는 User, Role, Audit과 같은 인증, 권한 과 관련된 기본 도메인들과 그 도메인이 사용하는 Audit이라는 클래스가 있었습니다. 그리고 User, Role에 대한 Dao, Service, Controller 까지도 들어있었죠.

문제는 Security가 저 녀석들을 사용하고 있다는 겁니다. User, UserDao를 사용합니다. 그렇게 되면 OSAF와 OSAF-App 두 번들이 CD에 빠집니다. 그래서 Security를 빼내면 될 줄 알고 저렇게 OSAF-Security를 빼내기로 결정.

실제 코드 작업을 좀 하다가 보니... 크헉!!!! osaf.service에서 osaf.security를 참조하고 있었습니다. 이러면 이거 때어낸다고 해서 해결될 문제가 아닌게 되는거라.. 다시 고민에 빠짐...

사용자 삽입 이미지
(이때부터 그림에 좀 신경을 쓰기 시작했죠.)

맨 왼쪽에 X 표를 친 부분이 바로 그 좌절하는 순간입니다.

여차저차해서 SessionFactory에 대한 실마리를 찾았고, 다시 OSAF는 좀 크지만, 한 덩어리로 가기로 했습니다.
사용자 삽입 이미지
(네모와 동그라미를 그리는 연습을 자주 해야겠습니다.)

실제 OSAF 내부에선 저런 순환 구조는 아닙니다. base쪽에 패키지를 세세하게 나눠뒀기 때문에 패키지 순환 참조는 발생하지 않습니다. CD는 하나도 없습니다.

오늘은 여기까지 구상하고 마치고 내일 다시 재도전해야겠습니다. "잘" 나누는 방법을 찾기란 이렇게 힘들고 재밌는 일이더군요. 캬캬캬.
top

TAG OSAF, OSGi
  1. moova 2008.08.19 03:48 PERM. MOD/DEL REPLY

    메모지에 끄적대기.. 좋은 습관이군요^^
    저도 저런 메모를 한 연습장이 ..(사실 스케치북)이지만 .. 저도 수권 나올 정도로 그림과 다이어그램은
    실전에서 항상 도움이 되더군요.^^
    머릿속에 맴돌고는 있지만 정리가 되지 않을 때 그리고 시스템을 경계하거나 분할 할때
    이런 것은 상당히 도움이 되더군요:). 통계형 sql문도 저런방법으로 그려서 몇백줄 되는 하나의 대용량 쿼리를 만들 수도 있습니다.. 쿨럭

    .(그림은 항상 유용하다!)

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2008.08.19 08:36 신고 PERM MOD/DEL

    넵.. 확실히 마우스로 UML 툴이나, PPT에서 네모 동그라미 그리는거 보다 손으로 그리는게 편하더라구요.

    문제는 그림은 어디까지나 그림이고, 코드로 검증하지 않으면 아무 소용없더라구요. 위에서도 그런 판단 미스를 코딩을 하다가 발견했기에 좀 더 진도가 나갈 수 있었습니다.

    (코드로 검증한 그림은 더욱 유용하다!) :)

Write a comment.


OSGi에서 Hibernate의 SessionFactory 문제

Spring DM/exercise : 2008.08.14 15:34


대체 어떻게 해야 할까? 뭘? @Entity 달려있는 클래스들이 여러 번들들에 분포되어 있고, 애플리케이션이 돌아가는 도중에 번들이 추가되고, 없어지고, 다시 설치되고, 업데이트 되는 와중에 SessionFactory는 그에 따라 계속 바껴야합니다.

Spring이 제공하는 AnnotationSessionFactoryBean 클래스로 만드는 SessionFactory는 정적입니다. 한 번 만들고 다른 빈들이 주입받아서 사용하는데, 도통 어떻게 변경해야 할지 모르겠습니다.

이게 문제는 OSGi, Hibernate, Spring DM, JPA를 사용하려다 보면 자연스럽게 다가오는 문제입니다. 저 말고도 이미 예전부터 이 문제를 당면한 여러 개발자들이 있었습니다. 지금 이 순간 "나는 iBatis를 쓰고 있어서 다행이야!!!" 라고 외치고 계신 분이 혹시 계신가요??? ㅎㅎㅎ 어림없습니다. 이 문제에서 못 벗어납니다. SessionFactory 대신에 SqlMapClient로 놓고 생각해 보시면 똑같습니다.

설정 파일을 번들 하나에 전부 넣어 놓고(애노테이션 붙인 도메인 객체들을 모두 한 번들에 놓고), 그걸로 SessionFactory 만들면 되지 않겠냐구요? 아니.. 미래에 추가될 번들에 들어있는 도메인을 어떻게 지금 추가할 수가 있나요? 백투더퓨쳐가 아닌이상 불가능할 뿐더러, 그럴 바엔 아예 OSGi 번들로 나누지 말고 그냥 기존의 애플리케이션처럼 사용하는게 좋을 거 같습니다. 뭐 잠깐 서버좀 껐다 키죠 뭐.ㅎㅎ

다행히도 이 문제에 대한 해결책이 나왔고, 그걸 구현한 예제까지도 제공하고 있습니다.
http://notehive.com/wp/2008/07/23/osgi-hibernate-spring-dm-sample/
전 이런 개발자가 정말 멋져보입니다. 이력서를 보니까, 88/89년에 인턴쉽 하고, 92년도부터 계속 개발을 해온 사람이네요. 그럼.. 지금.. 16년째.. 캬;;;; 장난 아니셤!!!
top

  1. jack 2010.04.23 17:22 PERM. MOD/DEL REPLY

    똑같은 문제에 봉착한 개발자 입니다 ㅠㅠ

    혹시 이 이슈에 대해서 해결을 하셨나요? 링크된 사이트의 소스를 적용해 봤지만

    No suitable driver found for jdbc 라는 exception만 뱉어네네요 ㅠ

    Favicon of http://whiteship.me BlogIcon 기선 2010.04.23 17:48 PERM MOD/DEL

    OSGi에서 손을 놓은지 오래되서 기억이 안나네요.
    http://whiteship.me/1854
    이 글을 보니까 어떻게 했던것 같긴한데 모르겠습니다. @_@

    하두 오래전에 살펴본 내용들이라 요즘은 훨씬 더 깔끔한 방법이 나오지 않았을까 싶네요.

Write a comment.


맥북에서도 Spring DM 웹 번들 설치 성공

Spring DM/exercise : 2008.07.31 00:03


인증샷 1. 번들 목록

사용자 삽입 이미지

인증샷 2. simple-web-app 첫 화면

사용자 삽입 이미지
인증샷 3. 서블릿

사용자 삽입 이미지
인증샷 4. JSP는.. 또 실패;;

사용자 삽입 이미지
흠... 리소스가 제대로 등록이 안 되있는건지.. 원인을 좀 찾아봐야겠습니다.


맥북에서 Spring DM 웹 번들 돌린게 왜 기쁘냐면요;; 안 해보신 분들은 몰라요... 윈도에서 돌리는 Equinox(줄여서 윈E)랑 맥에서 돌리는 Equinox(맥E)가 좀 차이가 나는 것 같습니다. 버그 같은데, 그게 맥E 의 버그인지, Spring DM web extender의 버그 인지, catalina의 버그인지 도통..잘 모르겠습니다.

spring web extender가 catalina 번들이 제공하는 서비스를 필요로 하는데, 만약에 catalina 보다 먼저, start 시키면 당연히.. resolved 상태로 못가고 해당 서비스가 들어올 때까지 좀 기다립니다. 그러다가 특정 시간이 되면 타임되서 넘어갑니다. 이게 정상이죠. 윈E에선 이렇게 정상적으로 동작합니다. 그래서 설치할 번들 목록에서 spring web extender 가 catalina 보다 위에 있어도(즉 먼저 start를 시도하겠죠.) 상관없습니다. 기다리다 보면, 다른 번들들 모두 Active 상태가 되고, spring web extender만 Resolved 상태로 남아있습니다.(catalina도 Active 상태가 됐으니 web extender가 필요로 하는 서비스가 제공 되서 상태가 변한겁니다.) 그러면... 이제 web extender만 다시 start 명령어로 Active 상태로 만들어 주면 됩니다.

하지만.... 맥E에선, 한 번 해보세요~ 해보셨어요? 안 해보셨으면 말을 하지마세요.

사용자 삽입 이미지
사용자 삽입 이미지


참.. 테스트 환경은. Eclipse 3.4. Spring DM 1.1.1 입니다.

ps: Bad case에 걸렸을 때 Good case를 찾아 빠져 나가는 방법이 있긴 있는데.. 그건 비밀입니다. 캬캬캬
top

Write a comment.


Late Binding in Java

Spring DM/exercise : 2008.07.20 17:04


참조 : http://neilbartlett.name/blog/osgibook/

OOP의 목적 중 하나는 의존성을 최대한 낮춰서 코드의 재사용성과 유연함을 늘리는 것이다.

자바에서는 인터페이스를 사용해서 이런 목적에 접근 했다. 인터페이스는 불편하고 그것을 구현한 구현체만 바꾸면 인터페이스를 사용하고 있는 클라이언트 코드는 변경할 필요가 없었기 때문이다. 하지만, 한계가 있는데, 객체를 생성하려면 어차피 구현한 클래스를 클라이언트 쪽에서 알고 있어야 한다는 것이다.

그래서, 스캐너의 생성자를 사용해서 문자열을 받고 그 문자열로 분기문을 돌려서 구현체를 기반으로 객체를 만드는 코드를 사용하기도 하는데, 역시나 새로운 구현체가 생기거나 하면, 또 코드를 바꿔야 된다.

이에 대한 대안으로 스캐너를 사용하지 않고, "다른 뭔가"가 그 객체를 제공해주게 하는 것이다. 그래서 등장한게 "Assembler" 다. "Assembler"를 여러 곳에서 만들어 쓰다가 패턴이 발견되었고, 그 패턴을 구현한 프레임워크가 스프링과 구글쥬스다.

이 들을 Dependency Injection 프레임워크라고 한다. (물론 스프링은 그게 전부는 아니지만) 불행히도, 이런 Assembler Pattern도 정적이라는 특성 때문에 몇 가지 문제가 있다. 객체들의 연관 관계를 정적으로 한 번 생성하고 말기 떄문에, 객체 생성 순서를 신경 써야 한다. B가 A 객체를 참조하려면 A 객체를 먼저 만들어야 된다. 그리고 객체간에 상호 참조(Circular Dependency)도 조심해야 된다.

또 다른 문젠 동적으로 업데이트 하는 것이 불가능하다. 이렇게 정적으로 묶여있는 의존성 그래프에는, 아주 작은 객체 연관 관계를 변경하려고 해도 전체 시스템을 껐다가 켜야 한다.

OSGi는 바로 이 문제를 동적인 "서비스"를 이용해서 해결한다.

서비스는 DI 프레임워크에서의 빈 처럼 평번한 자바 객체(POJO)다. 서비스는 하나 이상의 인터페이스 이름으로 OSGi 서비스 레지스트리에 의해 제공된다. 서비스는 다른 서비스를 사용할 수 있고 고정적인 그래프로 묶이는게 아니라, 서비스는 언제든지 동적으로 등록되고 해지될수 있다. 따라서 서비스들 사이의 관계는 임시적인 관계이다.

객체 생성 순서 문제는 다음과 같이 해결한다. B가 A를 필요로 하는 상황에서 B 객체를 만들 때 A 객체가 있는지 화인하고 안 만들어져있으면 이 객체를 이용할 수 있을 때 까지 잠시 대기한다. 그리고 A 객체를 B가 이용하고 있는 도중에 A가 사라지고 새로운 A' 객체를 등록하면 서비스 B한테 이벤트를 날려서 교체하게 해준다.


인터페이스 -> Scanner -> Assembler -> DI Framework -> OSGi


top

  1. 선영욱 2008.07.21 00:42 PERM. MOD/DEL REPLY

    아~OSGi가 그런 내용이였군요.
    저는 옛날부터 뭔가 필요한 때가 아니면 관심도, 찾을려고도 안해서 기선님의 블로그에서 요즘 OSGi만 나왔는데도 Pass했었는데, 윗 글을 통해서 중요한 정보를 얻은 것 같습니다.
    이거~기선님 아티클 처음부터 읽어봐야 겠네요. ^^;;
    감사합니다.

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2008.07.21 00:53 PERM MOD/DEL

    사실 제 아티클이라고 할 순 없죠. 전 그냥 제가 읽은 것을 요약한 것일 뿐, 저 글들은 참조 링크에 걸어둔 본문을 참조 요약한 것입니다.

Write a comment.


bnd에 번들 실행환경 설정하기

Spring DM/exercise : 2008.07.13 09:29


Bundle-RequiredExecutionEnvironment: J2SE-1.5, J2SE-1.4

이런식으로 설정하면 이 설정 그대로 MANIFEST.MF에 복사해서 붙여넣어줍니다. 모든 번들에 이런 실행환경을 설정해주는것이 좋겠죠. 가용한 설정 값드은 다음과 같습니다.

CDC-1.0/Foundation-1.0
CDC-1.1/Foundation-1.1
JRE-1.1
J2SE-1.2
J2SE-1.3
J2SE-1.4
J2SE-1.5
J2SE-1.6
OSGi/Minimum-1.0
OSGi/Minimum-1.1

1.5 환경에서 1.4 용 번들을 만들고 싶을 때, -source 와 -target을 사용해서 컴파일 하면 되지만, -source는 애노테이션이나 제네렉, for-each문과 같은 기능을 꺼버리고, -target은 클래스 파일 버전을 1.4 환경이 읽을 수 있도록 설정하는 것일 뿐, 실제 API 상의 차이는 잡아내지 못합니다. 예를 들어, String의 contains() 메소드는 1.5에 추가되어서 JDK 1.5 이상이 환경에서 얼마든지 저 메소드를 사용할 수 있습니다. 저 코드를 담고 있는 번들을 -target과 -source를 사용해서 1.4 번들로 만들고 JDK 1.4 위에서 돌리면, 예외가 발생합니다. 당연한거죠. 따라서, 1.4 용 번들을 만들고자 한다면, JDK 1.4 위에서 해당 번들을 만들고 패키징하는 것을 권장합니다.
top

Write a comment.


OSGi에서 클래스 로딩 순서

Spring DM/exercise : 2008.07.13 09:16


사용자 삽입 이미지

먼저, java.* 에 들어있는 클래스거나, org.osgi.framework.bootdelegation 속성에 설정된 클래스면 Parent Class Loader에게 클래스로딩 책임을 위임합니다.

다음은 Import-Packaged에 명시된 패키지에 들어있는 클래스라면, 클래스로딩 책임을 해당 클래스를 Export 한 번들의 클래스로더에게 위임합니다.

다음은 Required-Bundle 설정으로 참조한 패키지에 들어있는 클래스라면, 클래스로딩 책임을 해당 번들의 클래스로더에게 위임합니다.

다음으로 자기 자신 내부에 해당 클래스가 있는지 찾습니다.

다섯번째는 자신에게 붙어있는 Fragment에서 해당 클래스를 찾아봅니다. 클래스로더는 자신의 클래스로더를 사용합니다.

여섯번째는 위 그림에 빠져있는데 동적 클래스로딩과 관련이있습니다. 이 부분은 나중에 살펴봅니다.

참조 : http://neilbartlett.name/blog/osgibook/
top

Write a comment.


버전 매기기와 범위 설정

Spring DM/exercise : 2008.07.12 00:56


버전 매기기

버전은 다음과 같이 구성되어 있다.

major.minor.micro.qualifier

major.minor.micro는 숫자고, qualifier는 알파벳이나 숫자를 사용한다. major 이 외에는 전부 생략이 가능하다. 예를 들어, 1 은 1.0 이고 이건 다시 1.0.0 이랑 같다. 다음으로 중요한 건 높낮이. 1.0 보다 2.0 이 높은 버전이라는 것에는 이견이 없을 것이며 실제로도 그렇다. 그러나 qualifier 쪽으로 가면 그렇게 쉽진 않다.

A. 1.0.0.whiteship1
B. 1.0.0.whiteship2
C. 1.0.0.whiteship10

이 셋 중에서 가장 높은 버전은 몇 일까? C라고 예상하신 분들도 있겠지만, 정답은 B다. Java의 String 클래스의 compareTo()로 비교해서 높은 값이 높은 버전이 된다. String의 compareTo()는 한 글자씩 왼쪽에서 부터 비교한다. 따라서 B < C < A 순으로 높은 버전이 된다.

버전 범위 설정하기

버전 범위는 Import-Package 또는 Required-Bundle 헤더에서 사용한다. 명시하지 않으면 기본 값은 다음과 같다.

"[0.0.0, *]"

0.0.0 부터 모든 버전을 나타낸다. Export-Package 또는 Bundle-Version에 버전을 명시하지 않은 경우 기본값으로 0.0.0이 설정되기 때문에 양쪽에서 모두 기본값을 사용하는 경우 문제없이 참조할 수 있다. 하지만 역시 버전은 명시적으로 관리해주는 것이 좋겠다.

[] 와 ()의 차이만 알면 되겠다. []는 이상 이하. ()는 초과 미만의 개념이다. [1.0.0, 2.0.0) 이렇게 설정되어 있다면 1.0.0 버전이 있으면 그걸 참조하고 2.0.0 만 있다면 그건 참조하지 않는다. 1.9.9와 1.0.0 두 가지 버전이 존재할 땐 높은 버전의 번들을 선택한다.

그렇다고 매번 "[1.0.0, 1.3.0]" 이런식으로 표기해야 하는 것은 아니다. Spring 2.5 이상 어떤 버전이든 관계가 없을 때는 그냥 "2.5." 라고 설정하면 OSGi 프레임워크는 "[2.5.0, *]" 이렇게 인식할 것이다. 따라서 상위 호환이 보장되는 라이브러리를 참조하고 있다면 버전을 저런식으로 명시해도 된다.

이와 반대로 특정 버전 하나 만 참조하는 라이브러리가 있을 수도 있다. 전~혀 상위나 하위 호환이 되지 않아서 딱 그 버전만 써야 한다면, 그때는 "[2.5.5, 2.5.5]" 이렇게 범위를 딱 해당 버전으로만 좁혀줘야 한다.

top

Write a comment.


OSGi 시작은 상호 참조 제거부터

Spring DM/exercise : 2008.07.11 09:49


OSGi와 Spring DM을 익히는 건 개인적으로 이제 어느 정도 올라왔기 때문에, 세세한 부분과 예제 실습만 틈틈히 하면 별 무리가 없다고 생각이 된다. 그러나 기술을 익히면서도 내심 드는 의문 '이 기술로 내가 지금 만들고 있는 애플리케이션을 어떻게 쪼개서 묶어야 되지?' 라는 것에는 쉽게 답을 내릴 수도 없었고 이리 저리 생각을 하고 고민을 해도 쉽게 결론이 나질 않았다. 기준이 세워지지도 않고... 그러던 중 토비 사부에게 유겐 휄러가 스프링 프로젝트 패키지 구조를 정리한 일에 대해 들었고, 그게 번들화의 시작이라는 느낌이 강하게 들었다.

일단 어떻게 쪼갤까 하는 고민은 많은 시행착오와 경험이 필요한데, 그 전에 우선 쪼갤 수 있는 상태의 프로젝트를 만드는게 시작이라는 것이다. 쪼갤 수 있는 프로젝트라는 건 순환 참조가 없어야 수월하고 깔끔한데, 그와 관련된 매우 좋은 글을 2006년 12월에 사부님이 블로그에 올린적이 있었다. 그 당시 나는 잘 기억도 안 나지만 너무 길고 뭔 내용인지 몰라서 안 읽고 지나갔을 것이다. (사실 이렇게 pass한 사부님의 글이 한 두개가 아니다.) 이제사 읽어보니 아주 쏙쏙 들어오고 글이 긴 건지도 모를 만큼 몰입되서 읽을 수 있었다.

다음 프로젝트 연기로 인해 느슨해진 요즘 새로운 목표가 생겼다. 현재 거의 마무리 단계인 애플리케이션을 잘개 쪼개서 번들로 만들고 S2AP 위에 돌리는 것이다. 해보자. 파이팅!!!

1. 순환 참조 탐색 및 정리
2. 번들로 쪼개기
3. S2AP 위에 돌리기

간단하지 아니한가. 캬캬캬 해보는거다.

top

  1. Favicon of http://benelog.egloos.com BlogIcon benelog 2008.07.16 11:05 PERM. MOD/DEL REPLY

    CBD나 OSGi나 아직 잘 모르기는 하지만, OSGi번들을 쪼개는 단위는 결국 CBD에서 말하는 컴퍼넌트의 설계와 비슷해지지 않을까.. 하는 생각이 듭니다.
    예전에 CBD로 프로젝트를 했을 때, 컴퍼넌트를 어떻게 잡아야 하는지에 대해 경험이 없이 처음 설계를 했었는데, 너무 잘게 다른 부분은 외부와의 찾은 참조로인해 의존관계가 너무 심해서 컴포넌트의 의미가 없어진 모듈도 있었습니다. 컴퍼넌트 끼리도 한방향으로만 참조를 하도록 설계를 해야 하는데, 처음 설계와는 다르게 뒤에 들어온 개발자가 컴퍼넌트의 의미를 생각하지 않고 다른 쪽의 모듈을 막 참조하는 경우도 있었죠. OSGi를 처음봤을 때 잘만 하면 CBD에서 말하는 컴퍼넌트 단위를 쪼개는 의미를 더욱 더 명확히 할 수 있지 않을까.. 하는 생각을 했었습니다.

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2008.07.16 11:41 신고 PERM MOD/DEL

    네. 저도 OSGi를 공부할 수록 점점 CBD에 관심에 가더라구요. 어떻게 하면 잘 쪼갤고 묶을지 말이죠. 그에 대한 힌트를 CBD에서 얻을 수 있지 않을까 기대하고 있습니다.

    개발자들이 커밋하기 전에 JDenpend로 각자 자신이 작성한코드의 의존성을 검증하도록 규칙을 정해 주셔야겠네요. 만약 이를 어기면 저녁을 쏜다던가 하는 식으로..ㅋㅋ

Write a comment.


Required-Bundle을 비추하는 이유

Spring DM/exercise : 2008.07.10 23:04


먼저 Required-Bundle이 뭔지 알아야겠다. Import-Package를 이해했으면 이건 뭐 아주 간단하다.

1. Resolved 상태가 되기 위한 조건

Import-Package 헤더에 명시한 패키지들이 어떤 번들들에 의해서 Export-Package 헤더이 설정 되어 있으면 해당 번들은  RESOLVED가 될 것이다.(물론 필수 였다는 가정하에.)

Required-Bunlde은 번들 단위로 바꿔서 생각하면 된다. 이 번들의 Required-Bundle에 설정한 번들이 RESOLVED 상태가 되어야 이 번들도 RESOLVED 상태가 될 수 있다.

2. Import 하는 패키지의 정적이냐 동적이냐

Import-Package로 패키지를 참조하면, 해당 패키지들만 참조할 수 있다. Required-Bundle로 참조하면 거기에 설정한 번들이 Export-Package에 명시한 모든 패키지들을 Import 한다. 따라서, Required-Bundle에 설정한 번들이 공개하는 패키지의 설정이 바뀌면 이 번들이 가져오겠다고 선언하는 패키지들도 달라지는 것이다.

자 그럼 이제 이 설정을 비추하는 이유를 살펴보자.

1. 위에서도 언급했듯이 Import 대상이 되는 패키지가 변한다. 그게 문제가 될 수 있다. 만약 이 번들이 꼭 필요로 하는 패키지를 저 번들이 가지고 있었는데, 저 번들이 갑자기 그 패키지를 Export-Package 목록에서 빼버렸다. 그래도 저 번들은 RESOLVED가 되었고, 그랬기 때문에 이 번들도 따라서 RESOLVED가 되었다. 하지만 이 번들이 동작하다가 ClassNotFoundException이나 NoClassDefinitionError를 발생시킬 여지가 다분하다.

2. 만약 Required-Bundle에 설정한 번들의 기능이 너무 많아져서 분리해야 된다고 생각해보자. 그럼 Requied-Bundle에 이 번들을 설정했던 모든 (소비자 격인) 번들들의 설정을 전부 고쳐줘야 한다. 얼마나 힘들고 고된 작업인가... 그냥 Import-Package를 썼었다면, 기능이 많아져서 분리를 해도 그냥 Export-Package도 같이 분리해서 가지고 가기만 하면 된다. 소비자 입장에선 아무것도 바꾸지 않아도 된다.

3. 낭비다. 필요 없이 Import 하는 패키지가 생길 것이다.

Required-Bundle 은 OSGi R4에 추가된 기능인데 Eclipse의 영향이 컸다고 한다.

top

Write a comment.


bnd 사용해서 API 가져오기(Import)

Spring DM/exercise : 2008.07.10 22:48


번들이 사용할 모든 라이브러리는 MANIFEST.MF 파일의 Import-Package에 기술 되어야 한다.

위의 문장은 맞는 문장일까 틀린 문장일까? 틀렸다. 예외가 있다. 바로 java.* 이하의 패키지들은 기술하지 않아도 된다. 별도의 import 없이도 사용할 수 있기 때문에 Import-Package에 굳이 명시할 필요가 없다. 하지만.. javax.*은 Import-Package에 명시해줘야 한다. javax.swing 이나 javax.awt  같은 것 들.. 또는 org.xml.sax와 같은 것들도 물론 명시적으로 선언해 줘야겠다.

그럼 bnd 설정 파일에는 어떻게 설정할까? bnd에도 Import-Package라는 헤더가 있고 필요한 모든 패키지를 import 하도록 * 로 설정해주면 된다.

Import-Package: *

이렇게.. 하지만 이미 이렇게 기본값(CoC)으로 설정되어 있어서 위의 설정을 아예 적지 않아도 알아서 저런 설정이 있는 것으로 인식해서 MANIFEST.MF 파일의 Import-Package를 만들어 준다. 좋다 좋아..

그래도 Import-Package: 헤더를 bnd  설정에서 사용해야 하는 경우가 있는데 바로. 버전을 명시할 때다. 개발 중이 아니라면 버전은 왠만해선 명시해 주는게 베스트 프랙티스라고 생각된다. 버전은 항상 범위로 설정된다. 보통 다음과 같다.

또 하나 필수 여부를 설정할 때도 Import-Package를 사용해야 한다. 기본은 필수 인데, 필수가 아니게 설정하고 싶을 때는 명시적 설정 해줘야 한다.

Import-Package: org.apache.log4j*;version="[1.2.0,1.3.0]",\
                         javax.swing*;resolution:=optional,\
                         *

위의 설정은 log4j 패키지 버전 1.2.0 이상 1.3.0 이하에 있는 클래스나 리소스를 참조하도록 설정했고, swing 패키지는 굳이 없어도 이 번들이 RESOLVE 상태가 되는데 지장이 없도록 설정했다. 마지막으로 그 외 필요한 모든 패키지들은 필수고 버전은 [0.0.0, *] 이렇게 설정되어서 어떤 버전이든 상관 없이 있기만 하면 된다.

마지막으로 Import-Package에 패키지 명을 명시적으로 선언하는 경우가 있는데, 특정 패키지를 아예 빼버리거나 무조건 추가한다. 왜일까? 특정 패키지를 빼는 경우는 번들의 코드 중에 일부는 절대로 실행이 되지 않을 꺼라는 전제가 있다면 그 부분에서 사용한 패키지를 굳이 Import 할 필요는 없을 것이다. 그리고 왜 bnd가 알아서 필요한 패키지를 찾아서 (MANIFEST.MF 에 있는) Import-Package 헤더에 추가해 줄텐데 명시적으로 등록을 하는 경우가 생길까? 그건 동적으로 로딩할 클래스 때문이다. 그런 클래스들을 담고 있는 패키지는 햇갈리게 * 를 사용하지 말고 명시적으로 전체 패키지 명을 적어주자.


top

Write a comment.


bnd 사용해서 API 공개(Export) 하기

Spring DM/exercise : 2008.07.10 22:17


Export-Package 헤더를 사용하면 된다. 예를 들어 다음과 같이..

Export-Package: whiteship.service.*;version=1.0.0

뒤에 버전은 해당 패키지를 1.0.0 버전으로 공개하겠다는 설정이다. 버전에 대해서는 조금 있다가 정리하자. bnd가 저 설정을 읽으면 다음 작업을 하게 된다.

1. 해당 JAR(번들) 안에 명시한 패키지가 들어있는지 검사한다.
2. MANIFEST.MF 파일에 대상이 되는 패키지를 Export-Package 헤더에 추가한다.

*와 !를 적절히 사용해서 다음과 같이 작성할 수 도 있다.

Export-Package: !com.*, *

com. 이하 모든 패키지는 제외하고 그 나머지 패키지들만 공개하겠다는 설정이다. 이 얼마나 간단한가.. 귿!
top

Write a comment.


BundleManager 0.2 번들 소스 코드

Spring DM/exercise : 2008.07.03 18:23


이전 버전과 달라진 건 클래스 하나.

BundleDirectoryManager.java
package whiteship;

import java.io.File;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;

public class BundleDirectoryManager implements BundleActivator {

    private static final long INTERBAL = 1000;
    private static String BUNDLE_DIRECTORY = "E:\\bundles";
    private volatile BundleContext context;

    private final Thread thread = new BundleManager(BUNDLE_DIRECTORY);

    public void start(BundleContext context) throws Exception {
        this.context = context;
        thread.start();
    }

    public void stop(BundleContext context) throws Exception {
        thread.interrupt();
    }

    protected Bundle findBundleByLocation(String location) {
        Bundle[] bundles = context.getBundles();
        for (int i = 0; i < bundles.length; i++) {
            if (bundles[i].getLocation().equals(location)) {
                return bundles[i];
            }
        }
        return null;
    }

    private class BundleManager extends Thread {

        private File bundleLocation;
        private List<String> bundleLocations = new CopyOnWriteArrayList<String>();

        public BundleManager(String location) {
            bundleLocation = new File(location);
        }

        public void run() {
            if (!bundleLocation.isDirectory())
                throw new RuntimeException(bundleLocation.getPath()
                        + " is not directory.");

            try {
                while (!Thread.currentThread().isInterrupted()) {
                    Thread.sleep(INTERBAL);
                    uninstallDeletedBundles();
                    installNewBundles(bundleLocation.listFiles(new JarFileFilter()));
                }
            } catch (InterruptedException e) {
                System.out.println("I'm going out");
            } catch (BundleException e) {
                throw new RuntimeException(e);
            }
        }

        private void uninstallDeletedBundles() throws BundleException {
            for(String bundleLocation : bundleLocations){
                File file = new File(bundleLocation.substring(5));
                if(file == null || !file.exists()){
                    findBundleByLocation(bundleLocation).uninstall();
                    bundleLocations.remove(bundleLocation);
                }
            }
        }

        private void installNewBundles(File[] bundleFiles) throws BundleException {
            for (File file : bundleFiles) {
                String bundleLocation = "file:" + file.getAbsolutePath();
                if(findBundleByLocation(bundleLocation) == null){
                    context.installBundle(bundleLocation);
                    bundleLocations.add(bundleLocation);
                }
                
            }
        }

    }
}

지정한 디렉터리에서 번들 파일을 삭제하면, 해당 번들은 OSGi 플랫폼에서 uninstall 하는 기능 추가. 짝짝짝 멋져부러~

이 멋진 모습을 동영상으로 찍어서 보여드리고 싶은데.. 일단 뭐 다 만든 다음에 보여드리도록 하죠.
top

Write a comment.


BundleManager 0.1 번들 소스 코드

Spring DM/exercise : 2008.07.03 14:36


BundleManager가 할 일
- 특정 폴더에 jar 파일을 집어 넣으면 그 녀석을 설치해야 한다.
- 이미 해당 jar 파일이 설치되어 있다면 설치하지 않는다.

BundleDirectoryManager.java
package whiteship;

import java.io.File;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;

public class BundleDirectoryManager implements BundleActivator {

    private static final long INTERBAL = 1000;
    private static String BUNDLE_DIRECTORY = "E:\\bundles";
    private volatile BundleContext context;

    private final Thread thread = new BundleManager(BUNDLE_DIRECTORY);

    public void start(BundleContext context) throws Exception {
        this.context = context;
        thread.start();
    }

    public void stop(BundleContext context) throws Exception {
        thread.interrupt();
    }

    protected Bundle findBundleByLocation(String location) {
        Bundle[] bundles = context.getBundles();
        for (int i = 0; i < bundles.length; i++) {
            if (bundles[i].getLocation().equals(location)) {
                return bundles[i];
            }
        }
        return null;
    }

    private class BundleManager extends Thread {

        private File bundleLocation;

        public BundleManager() {
            //TODO I need CoC
        }

        public BundleManager(String location) {
            bundleLocation = new File(location);
        }

        public void run() {
            if (!bundleLocation.isDirectory())
                throw new RuntimeException(bundleLocation.getPath()
                        + " is not directory.");

            try {
                while (!Thread.currentThread().isInterrupted()) {
                    Thread.sleep(INTERBAL);

                    for (File file : bundleLocation.listFiles(new JarFileFilter())) {
                        String bundleLocation = "file:" + file.getAbsolutePath();
                        if(findBundleByLocation(bundleLocation) == null)
                            context.installBundle(bundleLocation);
                    }
                }
            } catch (InterruptedException e) {
                System.out.println("I'm going out");
            } catch (BundleException e) {
                System.out.println("Error installing bundle");
                throw new RuntimeException(e);
            }
        }

    }
}

번들을 시작시키면 위의 클래스에 있는 start()가 실행되고, 주기적으로(현재 1초) 특정 폴더(현재 E:\bundles)를 확인해서 그 안에 들어있는 JAR 파일들을 찾아서 설치한다. 이 때 만약 이미 OSGi 플랫폼에 설치되어 있는 번들은 설치하지 않는다.

JarFileFilter.java
package whiteship;

import java.io.File;
import java.io.FileFilter;

public class JarFileFilter implements FileFilter {

    public boolean accept(File f) {
        if(f.isDirectory())
            return false;
       
        String extenstion = getExtension(f);
        if(extenstion != null && extenstion.equals("jar"))
            return true;
       
        return false;
    }

     public String getExtension(File f) {
            String ext = null;
            String s = f.getName();
            int i = s.lastIndexOf('.');

            if (i > 0 &&  i < s.length() - 1) {
                ext = s.substring(i+1).toLowerCase();
            }
            return ext;
        }

}

파일 필터로. 특정 폴더 안에 들어있는 파일들 중에 확장자가 jar 인 파일들만 가져오기 위해서 만들었음.

bnd파일
# bundleManager.bnd
Private-Package: whiteship
Bundle-Activator: whiteship.BundleDirectoryManager




개선하거나 생각해볼 것
- 스프링 DM 번들로 변경하자.
- 시간이랑 폴더는 DI가 가능하도록 변경하자.(기본 값 유지)
- jar 뿐만 아니라 war로 설치를 시도하게 하자. 스프링 DM 웹 번들일 수도 있으니까.
- getExtension() 메소드는 별도의 Util 클래스로 빼내기.
- 예외처리 RuntimeException으로 처리하기.

0.2
- 해당 폴더에서 jar 파일이 삭제되면, 해당 번들을 uninstall 시킨다.

0.3
- 만약 해당 폴더에 있는 jar 파일이 변경되면, 해당 번들을 update 시킨다.

참조: http://neilbartlett.name/blog/osgibook/
top

Write a comment.


HelloBundle 번들 소스 코드

Spring DM/exercise : 2008.07.03 14:23


HelloOsgi.java
package whiteship;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class HelloOsgi implements BundleActivator {

    public void start(BundleContext arg0) throws Exception {
        System.out.println("하이");
    }

    public void stop(BundleContext arg0) throws Exception {
        System.out.println("바이바이");        
    }

}
설명이 필요 없는 매우 매우 간단한 번들.

bnd 파일
# helloBundle.bnd
Private-Package: whiteship
Bundle-Activator: whiteship.HelloOsgi

저 번들을 start 시키면 화면에 "하이" 라는 메시지를 찍고 stop 시키면 "바이바이"를 출력함.


테스트 용도로 사용하기 좋습니다.
top

TAG HelloOsgi

Write a comment.


번들 라이프사이클

Spring DM/exercise : 2008.06.25 22:56


전에 한 번 그림도 그려보고 아드리안의 발표자료에서도 봤지만, 이번엔 Neil이 그린 라이프사이클입니다.

사용자 삽입 이미지

install 커맨드를 사용해서 번들을 설치하면 맨 먼저 INSTALLED 상태가 되고,
start 커맨드를 사용하면 번들이 필요로 하는 패키지나 서비스를 참조할 수 있는지 확인한 다음 RESOLVED 상태가 되고, 자동으로 자신이 공개할 서비스를 서비스 레지스트리에 등록하고 ACTIVE  상태가 됩니다.

이 상태에서 update 명령을 실행하면 일단 RESOLVED 상태로 넘어가야 하기 떄문에 STOPPING 과정을 거치는데 이 과정에서 자신이 등록했던 서비스를 서비스 레지스트리에서 제거하고 RESOLVED 상태에서 INSTALLED 상태로 변하고 여기서 실제 번들 파일을 update 합니다. 이 때 이미 번들이 위치한 정보를 알고 있기 때문에 위치를 다시 알려줄 필요는 없겠죠. 그런 다음 원래 상태가 ACTIVE 상태였기 떄문에 다시  start를 합니다. 그럼 처음과 같이 RESOLVED -> STARTING 을 걸쳐 ACTIVE 상태가 됩니다.

이 상태에서 이번에는 stop 커맨드를 사용하면 번들은 RESOLVED 상태가 됩니다. 왜? 이미 해당 번들이 필요한 서비스나 패키지들이 가용하다는 것을 검증한 상태기 때문이죠.

해당 번들을 아예 안 쓸꺼면 그 땐 uninstall 해버리면 됩니다. 현재 상태가 어떤 상태이든 관계없이 저 고리를 따라서 이동한 뒤 설치된 번들 목록에서 사라질 겁니다. 다음에 그 번들을 다시 설치하고 싶으면 위치를 또 알려줘야 합니다. 그게 update와 uninstall/install의 가장 큰 외관상 차이며, 개념적인 차이기도 합니다.

Anyway 무사히 모든 번들들이 RESOLVED 되기를 빕니다.

참조 : http://neilbartlett.name/blog/osgibook/

top

  1. 2008.11.17 18:53 PERM. MOD/DEL REPLY

    비밀댓글입니다

    Favicon of https://whiteship.tistory.com BlogIcon 기선 2008.11.17 21:48 신고 PERM MOD/DEL

    넹. Neil 이란 사람의 그림이라는 출처만 분명히 해주신다면문제 없을 것 같습니다.

Write a comment.


bnd를 소개합니다.

Spring DM/exercise : 2008.06.23 23:25


bnd는 겉보기에는 간단한 jar 파일 하나지만, 사실 이클립스 플러그인인데다가, Ant 태스크 정의를 가지고 있고, Maven 플러그인이며, 실제 독립적으로 어떤 기능을 수행하는 Java 애플리케이션이기도 하다. 그야말로 본좌라고 할 수 있다.

http://www.aqute.biz/Code/Download

위 링크에 가면 bnd-버전.jar 파일을 바로 다운받을 수 있습니다. 간단하군요. 다른 프로젝트들처럼 zip 파일로 샘플이며 문서며 소스 코드도 묶어두지 않고 바로 jar 파일입니다.

흠...사용법(콜솔 명령어, Eclipse 플러긴, Ant 스크립트, Maven 플러긴)은

http://www.aqute.biz/Code/Bnd

위의 링크 하단에 있습니다.

top

TAG Bnd, OSGi
  1. Favicon of http://gerions.egloos.com BlogIcon 윤걸 2008.06.24 09:04 PERM. MOD/DEL REPLY

    bnd 면 build and deploy 의 약자 정도 되려나?? 왠지 그런 생각이 드네.. ㅎㅎㅎ

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2008.06.24 10:13 PERM MOD/DEL

    그냥 번들 줄임말이요.ㅋㅋㅋ
    BuNDle.

Write a comment.


Eclipse에서 Felix 사용하기

Spring DM/exercise : 2008.06.22 11:39


참조: http://neilbartlett.name/blog/osgibook/

OSGi 스펙 4.0 구현체 중 하나로, 아파치 재단의 프로젝트입니다. Equinox가 OSGi 구현체로 많이 사용되고 있지만, Felix는 Equinox에 비해 상당히 Compact하게 구현한거라 jar 파일 용량이 반 밖에 안 되며, 스펙도 매우 철저하게 따르고 있기 때문에 Felix에서 구동한 번들이 Equinox나 Knopflepfish에서도 무난히 동작한다고 합니다.

1. 먼저 Felix를 다운로드 받습니다.

http://felix.apache.org/site/downloads.cgi

2. 다음 압축을 풀고 원하는 위치로 이동시킵니다.

저는 C:\java\felix-1.0.4 로 이동시켰습니다.

3. 이클립스에 라이브러리를 등록합니다.

사용자 삽입 이미지
위의 화면을 보시면 아시겠죠? Preferences에 Java -> Build Path -> User Libraries에 New를 클릭한 뒤 Felix라고 입력합니다.

사용자 삽입 이미지
다음은 실제 라이브러리를 추가하는 과정으로 Add Jar 버튼을 누른다음 2번 과정에서 옮겨둔 폴더로 이동한 다음 bin/felix.jar 파일을 선택해줍니다. 그럼 위의 화면과 같이 jar 파일이 추가된 걸 확인할 수 있습니다.

4. Felix 프로젝트 만들기

일반 Java 프로젝트 하나를 생성합니다.
사용자 삽입 이미지
이름에는 전 OSGi Felix라고 줬습니다. 다음 Next를 클릭합니다. 라이브러리를 추가하기 위해섭니다.

사용자 삽입 이미지
Add Library를 클릭하고 User Library를 클릭하면 3번에서 추가한 Felix 라이브러리를 선택할 수 있습니다.

5. 프로젝트 세팅하기

Felix를 구동하는데 필요한 번들과 설정파일을 복사해서 위에서 만든 프로젝트로 붙여넣어 줍니다. 해당 번들과 설정파일은 2번에서 작업한 폴더에 보시면 bundle과 conf라는 폴더 안에 들어있고 그 두 개의 폴더를 복사해서 붙여주면 됩니다.

사용자 삽입 이미지

사용자 삽입 이미지

다음은 설정파일을 변경해서 로그 메시지를 좀 더 많이 뿌리도록 설정합니다. 위에 보이는 config.properties파일을 열고 felix.log.level=4 를 felix.log.level=1로 변경해줍니다.

이제 프로젝트 세팅은 끝났습니다. 실행해 봐야겠죠.

6. 실행 환경 설정하기

사용자 삽입 이미지
Run Dialog로 들어가서 Java Application에 하나의 인스턴스를 만들고 Name에는 Felix를 주고, Project에는 위에서 세팅을 마친 프로젝트 OSGi Felix를 선택해줍니다. 다음 Main은 Search 버튼을 눌러보면 두 개의 클래스가 찾아지는데 그 중에서 아래에 있는 Main을 선택합니다.

사용자 삽입 이미지
자 그럼 이 상태가 되었고 이제 실행할 준비도 끝났습니다.

7. 실행하기

Run을 클릭합니다.

사용자 삽입 이미지

프로파일 이름을 달라고 하네요. 아무 이름이나 줍니다. 전 tutorial이라고 줬습니다.

사용자 삽입 이미지
그럼 뭔가가 설치되는게 보입니다. 뭔 뜻인지는 모르겠습니다. 패스.

현재 설치된 번들들의 상태를 참조하려면 ps를 입력합니다.
사용자 삽입 이미지
음.. 총 네개의 번들이 동작중이군요.

사용할 수 있는 명령어를 참조하려면 help를 입력합니다.

사용자 삽입 이미지

끝~
top

TAG Eclipse, Felix

Write a comment.


귿!! OSGi위에 웹 애플리케이션 돌리기 성공 with Spring DM

Spring DM/exercise : 2008.04.30 18:00


사용자 삽입 이미지

위 그림 한장이면 돌리는 방법은 끝나죠.

맨 아래 보이는 녀석이 Spring DM 받으면 src/sample에 들어있는 웹 애플리케이션입니다. 자세한 내용은 좀 더 공부해야겠지만 일단 예제를 돌려서 기쁘네요.



012

현재 Servlet 까지는 됐는데, JSP는 아직 안 됩니다. 이것 저것 만지다보면 될 것 같네요.

top

  1. Favicon of https://yunsunghan.tistory.com BlogIcon Max. 2008.05.02 09:51 신고 PERM. MOD/DEL REPLY

    축하~~~~해요 :) ㅎㅎ

    Favicon of http://whiteship.tistory.com BlogIcon 기선 2008.05.03 09:31 PERM MOD/DEL

    넹 ^^ 감사합니다.

Write a comment.


Spring Dynamic Modules Demos 실습

Spring DM/exercise : 2008.03.27 18:06


참조 : http://www.springframework.org/osgi/demos

한 시간 정도면 따라하실 수 있습니다. 스크린 캐스팅한 다음에 편집을 정말 잘 한 것 같습니다. 스크린 캐스팅에도 노하우가 필요할 것 같습니다. 어쨋거나..

Part1은 Integration Test 환경을 마련하는 과정입니다. 스프링이 자동으로 Menifest를 작성해주기 때문에 상당히 편하게 통합 테스트를 작성하실 수 있습니다.

OSGi 통합 테스트를 작성하는 이유는 Bundle이 실제로 서비스 레지스트리에서 서비스를 가져오는지 패키지를 import했는지 등을 확인하려면 일단 OSGi 플랫폼에 올라가야겠죠. 그래서 통합 테스트가 필요합니다. 스프링은 그런 테스트를 편하게 할 수 있도록 유용한 API를 제공하고 이 동영상은 그 API들을 사용하는 방법을 보여주고 있습니다. Eclipse와 Maven 그리고 Spring DM의 조합으로 말이죠.

문제는 동영상을 그대로 따라하면 분명히 잘 되지 않으실 겁니다.

1. Maven에서 이클립스 프로젝트를 만든다해고 Maven 로컬 저장소를 Eclipse에 알려주지 않으면 라이브러리 못찾아서 프로젝트에 에러뜹니다.
2. pom.xml와 log4j.properties파일에 오타를 입력할 가능성이 농후합니다. 화면 보고 따라서 쳐야하는데.. 눈아프죠.
3. pom.xml에 누락된 설정이 있습니다. 화면에는 빠져있는 설정이 있는데 그걸 찾아서 넣어줘야 합니다.
4. 콘솔에서 mvn eclipse:eclipse를 실행한 다음에는 반드시 프로젝트를 리프래쉬 해야합니다.(F5)
5. src/test에 log4j 프로퍼티 파일을 넣기만 해서는 이클립스가 읽지 않습니다.

Part2는 스프링의 application context에 bean 설정을 사용해서 서비스를 가져오는 테스트를 합니다. Part1의 문제들을 해결하고 진행하셨다면 무난하게 따라하실 수 있는데 딱 한가지 어려운게 있다면 역시나 xml 파일 root 엘리먼트 작성할 때 오타를 입력할 가능성이 농후하며 지루하다는 것입니다.

매우 좋은 동영상이고 편집도 잘되어 있습니다. 마치 장애물 달리기를 하는 기분으로 예제를 실습해 볼 수 있어서 재미있었습니다.
top

Write a comment.


설맞이 OSGi 간단 예제

Spring DM/exercise : 2008.02.06 16:46


1. Equinox 기반 OSGi 번들 만들기.
2. OSGi 번들 패키지 노출하기와 들여오기.
3. OSGi 서비스 노출하기와 들여오기.
4. Spring DM을 사용하여 서비스 노출하기.
5. Spring DM을 사용하여 서비스 사용하기.

가장 기본이 되는 것들만 살펴봤습니다.



top

Write a comment.


5. Spring DM을 사용하여 서비스 사용하기.

Spring DM/exercise : 2008.02.06 16:34


1. StoreManager 번들 정리하기.

- Activator를 없애버립니다.
- ServiceTracker도 없애버립니다.

둘다 일단 MENIFEST.MF의 Dependencies에서 제거하고 소스코드도 제거합니다.
이 번들이 필요한 건 오직 Store 번들의 Service 패키지에 있는 Greeting이라는 인터페이스 뿐입니다.

StoreManager의 MENIFEST.MF 파일이 다음과 같으면 깨끗하게 정리가 된 것입니다.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: StoreManager Plug-in
Bundle-SymbolicName: StoreManager
Bundle-Version: 1.0.0
Import-Package: service
Eclipse-LazyStart: true

2. 구현하기

Keesun이라는 클래스를 하나 만들고 다음과 같이 구현했습니다.

public class Keesun {

    private Greeting greeting;

    public void setGreeting(Greeting greeting) {
        this.greeting = greeting;
    }

    public void start(){
        System.out.println(greeting.hi("기선"));
    }
}

Setter Injection을 사용하기로 했고, start() 메소드는 제대로 세팅됐나 확인하기 위해 만들어 뒀습니다.

3. Bean 설정하기

META-INF 폴더에 spring 폴더를 만들고 스프링 설정 파일 하나를 만듭니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:osgi="http://www.springframework.org/schema/osgi"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <bean class="storemanager.Keesun" init-method="start">
        <property name="greeting" ref="greeting" />
    </bean>

    <osgi:reference id="greeting" interface="service.Greeting" />

</beans>

Keesun 클래스에 start라는 init-method를 등록해두고, <osgi:reference /> 엘리먼트로 서비스를 참조합니다. 그러면 이 내부에서는 마치 자기가 원래 가지고 있던 bean 처럼 사용할 수 있습니다.

4. 테스트

사용자 삽입 이미지

Run Dialog에서 Spring OSGi만 체크하고 Run 시킵니다. 나머지는 install을 사용해서 설치합니다.

osgi> install file:c:/plugins/Store_1.0.0.jar
Bundle id is 22

osgi> install file:c:/plugins/StoreManager_1.0.0.jar
Bundle id is 23

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      system.bundle_3.2.2.R32x_v20070118
1    ACTIVE      org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
2    ACTIVE      org.springframework.bundle.spring.context.support_2.5.1
3    ACTIVE      org.springframework.bundle.osgi.core_1.0.0
4    ACTIVE      org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
5    ACTIVE      org.springframework.bundle.osgi.extender_1.0.0
6    ACTIVE      org.springframework.bundle.spring.context_2.5.1
7    ACTIVE      org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
8    ACTIVE      org.springframework.bundle.osgi.extensions.annotations_1.0.0
9    ACTIVE      org.springframework.osgi.junit.osgi_3.8.2.SNAPSHOT
10    INSTALLED   org.springframework.bundle.osgi.test_1.0.0
11    ACTIVE      org.springframework.bundle.osgi.io_1.0.0
12    ACTIVE      org.springframework.osgi.asm.osgi_2.2.3.SNAPSHOT
13    ACTIVE      jcl104.over.slf4j_1.4.3
14    ACTIVE      org.springframework.spring.source_2.5.1
15    ACTIVE      org.springframework.bundle.spring.core_2.5.1
16    ACTIVE      org.springframework.osgi.source_1.0.0
17    ACTIVE      slf4j.log4j12_1.4.3
18    ACTIVE      org.springframework.osgi.backport-util-concurrent.osgi_3.1.0.SNAPSHOT
19    ACTIVE      org.springframework.bundle.spring.beans_2.5.1
20    ACTIVE      slf4j.api_1.4.3
21    ACTIVE      org.springframework.bundle.spring.aop_2.5.1
22    INSTALLED   Store_1.0.0
23    INSTALLED   StoreManager_1.0.0

osgi> start 22

osgi> GreetingImpl Bean을 만들었습니다.
start 23

osgi> hi 기선

끝~ 이 아니고 이제부터 시작입니다.
설특집은 끝입니다.


top

Write a comment.


4. Spring DM을 사용하여 서비스 노출하기.

Spring DM/exercise : 2008.02.06 15:23


1. Spring IDE 설치

http://www.springide.com/updatesite_nightly 를 이용해서 가장 최신의 Spring IDE 플러그인을 받을 겁니다. 그런데 처음 부터 저를 따라하셨으면, Eclipse를 새로 설치하셨을텐데, 그러면 Web Tools Plugin도 설치되어 있지 않아서, Spring IDE 플러그인을 설치하려고 하면, 종속성 문제 때문에 에러가 납니다.

그럴 때는 Eclispe Discovery Site도 같이 업데이트에서 선택한 다음에, Web 폴더를 왕창 선택해 줍니다.  스샷을 찍었어야 하는데, 깜빡하고 지나가서.. pass 합니다.

2. 필요한 번들 추가.

Target Platform 하단의 Pre Defined Target Plattform에서 Spring OSGi를 선택합니다. 그러면 끝입니다.

사용자 삽입 이미지

Spring IDE에서 제공해주는 기능입니다. 저걸 선택하면, 알아서 Spring OSGi를 사용할 때 필요한 번들들을 추가해 줍니다.

id    State       Bundle
0    ACTIVE      system.bundle_3.2.2.R32x_v20070118
1    ACTIVE      org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
2    ACTIVE      org.springframework.bundle.spring.core_2.5.1
3    ACTIVE      org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
4    ACTIVE      slf4j.api_1.4.3
5    ACTIVE      org.springframework.osgi.junit.osgi_3.8.2.SNAPSHOT
6    ACTIVE      org.springframework.osgi.source_1.0.0
7    ACTIVE      org.springframework.osgi.backport-util-concurrent.osgi_3.1.0.SNAPSHOT
8    ACTIVE      org.springframework.spring.source_2.5.1
9    INSTALLED   org.springframework.bundle.osgi.test_1.0.0
10    ACTIVE      org.springframework.bundle.spring.context_2.5.1
11    ACTIVE      org.springframework.bundle.osgi.io_1.0.0
12    ACTIVE      jcl104.over.slf4j_1.4.3
13    ACTIVE      org.springframework.bundle.osgi.extensions.annotations_1.0.0
14    ACTIVE      org.springframework.osgi.asm.osgi_2.2.3.SNAPSHOT
15    ACTIVE      org.springframework.bundle.spring.beans_2.5.1
16    ACTIVE      org.springframework.bundle.osgi.core_1.0.0
17    ACTIVE      org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
18    ACTIVE      org.springframework.bundle.osgi.extender_1.0.0
19    ACTIVE      slf4j.log4j12_1.4.3
20    ACTIVE      org.springframework.bundle.spring.aop_2.5.1
21    ACTIVE      org.springframework.bundle.spring.context.support_2.5.1

osgi.test 번들이 가동이 안 됐는데 뭔가 필요한게 없나 봅니다.

osgi> diag 9
initial@reference:file:plugins/spring-osgi-test-1.0.jar/ [9]
  Missing imported package org.apache.felix.framework_0.0.0.
  Missing imported package org.apache.felix.main_0.0.0.
  Missing imported package org.knopflerfish.framework_0.0.0.
  Missing imported package org.springframework.test_2.5.0.

확인해보니, 테스트 용도라서, 그런 것 같네요. 배포할 때 테스트 번들까지 추가할 필요가 없어서 그런가 봅니다.

암튼 패스하고 계속 진행하겠습니다.

3. 서비스 노출시키기

이전에 서비스를 노출할 때는, context.registerService() 라는 메소드를 사용했었습니다. 그런데 이제는 스프링 설정 파일에 bean으로 등록하면 됩니다.

먼저, MANIFEST.MF 파일을 수정합니다. overview탭에서 Activator를 지웁니다.(소스코드에도 Activator를 지워버립니다.)
사용자 삽입 이미지
Activator 클래스는 아예 지워버립니다.

그리고 META-INF 폴더 밑에 spring 폴더를 만들고 springOSGi.xml 이라는 이름으로 파일을 만들어 줍니다. 꼭 이 이름일 필요는 없습니다. spring 폴더 밑에 있는 xml 파일을 전부 읽어 들일테니까요.

그리고 다음과 같이 XML을 작성합니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:osgi="http://www.springframework.org/schema/osgi"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

    <bean id="greeting" class="service.impl.GreetingImpl" init-method="start" />
   
    <osgi:service interface="service.Greeting" ref="greeting" />
   
</beans>

bean이 만들어졌는지 확인하기 위해서, init-method 속성을 사용했습니다. GreetingImpl에는 start 메소드 하나를 추가해줍니다.

public class GreetingImpl implements Greeting {

    public String hi(String name) {
        return "hi " + name;
    }
   
    public void start(){
        System.out.println("GreetingImpl Bean을 만들었습니다.");
    }

}

그리고 이 번들을 플랫폼에 올리면 다음과 같이 applicationContext가 만들어 진 것을 확인할 수 있습니다.

osgi> log4j:WARN No appenders could be found for logger (org.springframework.util.ClassUtils).
log4j:WARN Please initialize the log4j system properly.
GreetingImpl Bean을 만들었습니다.
ss

Framework is launched.

id    State       Bundle
0    ACTIVE      system.bundle_3.2.2.R32x_v20070118
1    ACTIVE      org.springframework.osgi.log4j.osgi_1.2.15.SNAPSHOT
2    ACTIVE      org.springframework.bundle.spring.context.support_2.5.1
3    ACTIVE      org.springframework.bundle.osgi.core_1.0.0
4    ACTIVE      org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT
5    ACTIVE      org.springframework.bundle.osgi.extender_1.0.0
6    ACTIVE      org.springframework.bundle.spring.context_2.5.1
7    ACTIVE      org.springframework.osgi.cglib-nodep.osgi_2.1.3.SNAPSHOT
8    ACTIVE      org.springframework.bundle.osgi.extensions.annotations_1.0.0
9    ACTIVE      org.springframework.osgi.junit.osgi_3.8.2.SNAPSHOT
10    INSTALLED   org.springframework.bundle.osgi.test_1.0.0
11    ACTIVE      org.springframework.bundle.osgi.io_1.0.0
12    ACTIVE      org.springframework.osgi.asm.osgi_2.2.3.SNAPSHOT
13    ACTIVE      jcl104.over.slf4j_1.4.3
14    ACTIVE      org.springframework.spring.source_2.5.1
15    ACTIVE      org.springframework.bundle.spring.core_2.5.1
16    ACTIVE      org.springframework.osgi.source_1.0.0
17    ACTIVE      slf4j.log4j12_1.4.3
19    ACTIVE      org.springframework.osgi.backport-util-concurrent.osgi_3.1.0.SNAPSHOT
20    ACTIVE      org.springframework.bundle.spring.beans_2.5.1
21    ACTIVE      slf4j.api_1.4.3
22    ACTIVE      org.springframework.bundle.spring.aop_2.5.1
23    ACTIVE      Store_1.0.0

osgi>

끝! 그냥 서비스로 제공할 클래스를 bean으로 등록한 다음에 <osgi:service/> 엘리먼트의 interface와 ref 속성으로 해당 bean을 서비스로 등록해둡니다.

top

Write a comment.


3. OSGi 서비스 노출하기와 들여오기.

Spring DM/exercise : 2008.02.06 12:52


1. Runtime 수정하기.

먼저 service.impl 패키지는 노출시키지 않고, service 패키지만 노출 시킵니다. 그 안에 들어있는 인터페이스만 외부로 노출 시키려는 것이지요.

사용자 삽입 이미지

이거면 됩니다. 그러면 이제 더 이상, StoreManager에서 service.impl 패키지를 import 할 수 없습니다. 따라서 구현체는 사용할 수 가 없게 됩니다. 구현체는 제공하지 말고 서비스를 registry에 등록하겠습니다.

2. Service Registry에 등록하기.

Activator로 이동해서. Store 번들이 ACTIVE 상태가 될 때, 특정 서비스를 등록하도록 코딩합니다.

public class Activator implements BundleActivator {

    public void start(BundleContext context) throws Exception {
        System.out.println("Store 번들을 가동 했습니다.");
        String className = Greeting.class.getName();
        context.registerService(className, new GreetingImpl(), null);
        System.out.println("GreetingImpl 객체를 '" + className + "'으로 서비스 레지스트리에 등록 했습니다.");
    }
   
    public void stop(BundleContext context) throws Exception {
        System.out.println("Store 번들을 멈췄습니다.");
    }

}

context에, 서비스를 등록합니다. 인터페이스의 이름으로 구현체를 등록합니다. JNDI와 비슷한 것 같네요.

자 이제 이 번들을 다시 Export 하고, start 시켜봅니다.

ACTIVE 상태가 된 다음에 서비스가 등록 된 것을 확인할 수 있습니다.

3. Dependencies 수정하기.

이제 StoreManager를 수정해야 합니다. 먼저 Depedencies에서 service.impl을 제거해 줍니다. 인터페이스만 알고 있으면 되기 때문에, service만 남겨둡니다.

사용자 삽입 이미지

이렇게 수정해면, GreetingImpl 클래스를 참조할 수 없기 때문에 에러가 발생하는데, 이 때 이상하게 Greeting 까지 못 찾는다고 에러가 납니다. 이건 Eclipse의 버그 같습니다. 이럴 때는 그냥 Eclipse를 껐다가 다시키면, Greeting 인터페이스를 참조할 수 있습니다.

이제 Activator에서 GreetingImpl 객체를 직접 만들어 사용하던 코드를 context에서 서비스를 가져오도록 수정해야 합니다.

서비스를 가져오려면 ServiceTracker를 사용해야 하는데, 그러려면, StoreManager에 org.osgi.util.tracker 패키지를 import 해주어야 합니다.

사용자 삽입 이미지

4. ServiceTracker 사용하기

이제 구현해 줍니다.

public class Activator implements BundleActivator {
   
    private ServiceTracker greetingServiceTracker;

    public void start(BundleContext context) throws Exception {
        System.out.println("StoreMaganer 번들 가동했습니다.");

        String serviceName = Greeting.class.getName();
        greetingServiceTracker = new ServiceTracker(context, serviceName, null);
        greetingServiceTracker.open();
        Greeting greeting = (Greeting) greetingServiceTracker.getService();
        System.out.println("'" + serviceName + "' 서비스를 가져왔습니다.");
       
        System.out.println(greeting.hi("토비"));
    }
   
    public void stop(BundleContext context) throws Exception {
        System.out.println("StoreManager 번들 멈춥니다.");
       
        greetingServiceTracker.close();
        greetingServiceTracker = null;
       
        System.out.println("Service Tracker 닫습니다.");
    }

}

ACTIVE 상태가 되면, 서비스 트래커를 만들고, 열어 줍니다. RESOLVED 상태가 될 때(stop)는 닫은 다음에 null로 만들어 줍니다.

그리고 Store 번들을 시작한 다음에 StoreManager를 시작하면 잘 동작합니다.

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828
1    RESOLVED    StoreManager_1.0.0
2    RESOLVED    Store_1.0.0

osgi> start 2
Store 번들을 가동 했습니다.
GreetingImpl 객체를 'service.Greeting'으로 서비스 레지스트리에 등록 했습니다.

osgi> start 1
StoreMaganer 번들 가동했습니다.
'service.Greeting' 서비스를 가져왔습니다.
hi 토비

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828
1    ACTIVE      StoreManager_1.0.0
2    ACTIVE      Store_1.0.0

osgi>

자 이 상태에서 Store를 stop하고, StoreManager도 stop한 다음에 StoreManaegr를 먼저 start 시킬 수 있습니다. 그럴 수도 있겠죠. 아직 작업이 마무리 되지 않았는데, 접속을 시도하고 있다고 생각해시면 그럴 수도 있다는 것이 상상되실 겁니다. 이런 상황에서 어떤 일이 벌어지는지 보겠습니다.

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828
1    ACTIVE      StoreManager_1.0.0
2    ACTIVE      Store_1.0.0

osgi> stop 2
Store 번들을 멈췄습니다.

osgi> stop 1
StoreManager 번들 멈춥니다.
Service Tracker 멈춥니다.

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828
1    RESOLVED    StoreManager_1.0.0
2    RESOLVED    Store_1.0.0

osgi> diag 1
file:/c:/plugins/StoreManager_1.0.0.jar [1]
  No unresolved constraints.

osgi> start 1
StoreMaganer 번들 가동했습니다.
'service.Greeting' 서비스를 가져왔습니다.
org.osgi.framework.BundleException: Exception in storemanager.Activator.start() of bundle StoreManager.
    at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:1018)
    at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:974)
    at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:346)
    at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:260)
    at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:252)
    at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:260)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
    at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:291)
    at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:276)
    at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:218)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
    at storemanager.Activator.start(Activator.java:22)
... 생략

재밌습니다. 분명 StoreManager가 Resolved 상태였는데(종속성이 모두 해결되고 Start 될 수 있는 상태) start를 시키면... NullPointerException이 발생합니다.

왜 그러죠?


당연하죠. 서비스 레지스트리에 StoreManager의 Activator에서 찾으려는 Service가 없기 때문이죠.

왜 없죠?

Store 번들이 stop 으로 인해 RESOLVED 상태가 되면서, 등록됐던 서비스들이 사라져버려서 그렇습니다.
증거는 아래를 열어보세요.

뭔가 멋지지가 않습니다.

5. 좀 더 멋지게 ServiceTracker 사용하기.

일단 StoreManager가 Resolved 상태가 됐다면, Start 시켜서, AVTIVE 상태로 만들고 싶습니다.(서비스가 없더라도 말이죠.) 그러다가 원하는 서비스가 등록되면, 그 때 서비스를 얻어낸 다음 할 일을 처리하게 하고 싶습니다.

public class Activator implements BundleActivator {

    public static class GreetingServiceTracker extends ServiceTracker {

        public GreetingServiceTracker(BundleContext context) {
            super(context, Greeting.class.getName(), null);
        }

        public Object addingService(ServiceReference reference) {
            Greeting greeting = (Greeting) context.getService(reference);
            System.out.println(greeting.hi("토비"));
            return greeting;
        }
    }

    private ServiceTracker greetingServiceTracker;

    public void start(BundleContext context) throws Exception {
        System.out.println("StoreMaganer 번들 가동했습니다.");

        greetingServiceTracker = new GreetingServiceTracker(context);
        greetingServiceTracker.open();
    }

    public void stop(BundleContext context) throws Exception {
        System.out.println("StoreManager 번들 멈춥니다.");

        greetingServiceTracker.close();
        greetingServiceTracker = null;

        System.out.println("Service Tracker 멈춥니다.");
    }

}

ServiceTracker의 addingService() 메소드는 ServiceTracker 객체를 만들 때 필요한 ServiceTrackerCustomizer 파라미터가 null일 때 호출 됩니다. 그래서 위처럼 생성자에서 super를 사용해서 세 번째 인자에 null을 넣어 주어야 addingService() 메소드가 호출되고, 그럼 이 메소드 안에서 context.getService(ServiceReference);를 사용해서 서비스 객체를 가져옵니다. 여기서 비밀이 있는 것 같은데 우선은 복잡한 건 제끼고 예제부터 실행해 보겠습니다.

이번에도 위에서 했던 것처럼, Export 한 다음, StoreManager와 Store를 stop 시키고, StoreManager를 먼저 start 시켜보겠습니다.

osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828
1    RESOLVED    StoreManager_1.0.0
2    RESOLVED    Store_1.0.0

osgi> start 1
StoreMaganer 번들 가동했습니다.

osgi> start 2
Store 번들을 가동 했습니다.
hi 토비
GreetingImpl 객체를 'service.Greeting'으로 서비스 레지스트리에 등록 했습니다.

osgi>

와우~~ 멋지지 않나요.

뭐가 멋진지 모르겠다구요??

분명 StoreManager를 먼저 실행 시켰는데 에러가 발생하지 않았습니다. 멋지지 않아요??? 물론 여기까진 별로 멋지지 않을 수도 있습니다. 그런데.. Store를 start 시키자 마자. StoreManager에서 해야 했던 일이 실행됐습니다.

이건 정말 멋진겁니다.

그런데 코딩이 정말... 피곤하죠. 이제 Spring DM이 등장할 차례 입니다.

참조 : http://www.eclipsezone.com/eclipse/forums/t91059.html
top

Write a comment.


2. OSGi 번들 패키지 노출하기와 들여오기.

Spring DM/exercise : 2008.02.06 11:13


1. 앞에서 만든 Store에 간단한 인터페이스 하나와 그걸 구현한 클래스를 만듭니다.


public interface Greeting {

    public String hi(String name);
}


public class GreetingImpl implements Greeting {

    public String hi(String name) {
        return "hi " + name;
    }

}

사용자 삽입 이미지

service 패키지에 Greeting 인터페이스, service.impl 패키지에는 GreetingImpl 클래스를 두었습니다. 그리고 이 두 개의 패키지 안에 있는 클래스들을 다른 번들에서 사용할 수 있도록 Export 해주어야 합니다.

2. 패키지 Export 하기

MENIFEST.MF 파일을 클릭하고, 하단의 Runtime 탭을 클릭하고, Export packages에서 Add 버튼을 클릭하여 밖으로 노출시킬 패키지들을 추가해줍니다. Alt + S로 저장해 줍니다.

사용자 삽입 이미지

3. 확인하기

위 번들을 다시 Export 해서, 다시 C:\plugins 폴더에 Store_1.0.0.jar 파일을 갱신합니다. 지금은 파일만 수정했을 뿐, OSGi 플랫폼에 올라가있는 번들은 수정되지 않았습니다.

지금 올라가 있는 Store 번들은 이클립스로 보고 있는 프로젝트의 번들이 아니라, 이전 글에서 Export 했던 Jar 파일을 기반으로 올라간 번들입니다. 그렇기 때문에, 방금전에 다시 Export를 한 것입니다. 어쨋든 좀 햇갈릴 수도 있겠지만, 지금까지 잘 따라오셨다면 다음과 같이 번들을 업데이트 할 수 있습니다.

update [id] 명령은 원래 RESOLVED 상태에서 수행됩니다. 그런데 만약 현재 번들이 ACTIVE 상태였다면, 해당 번들을 STOP으로 멈춘다음에 update를 수행하고, 다시 START 시켜줍니다.(똑똑한 Equinox, 나이스 OSGi 스팩)

사용자 삽입 이미지

현재 번들이 service 패키지와 service.impl 패키지를 노출시키고 있는 것을 확인했습니다.

4. 새로운 번들 만들기

위의 번들을 사용할 새로운 번들을 만듭니다. 첫 번째 글을 참조하시면 쉽게 만들 수 있을테니 생략하겠습니다.번들 이름은 편의상 StoreManage라고 하겠습니다.

5. Store 번들의 패키지 사용하기

StoreManager 번들의 Actovator를 다음과 같이 구현하려고 합니다.

public class Activator implements BundleActivator {

    public void start(BundleContext context) throws Exception {
        System.out.println("StoreMaganer 번들 가동했습니다.");
        Greeting greeting = new GreetingImpl();
        System.out.println(greeting.hi("기선"));
    }
   
    public void stop(BundleContext context) throws Exception {
        System.out.println("StoreManager 번들 멈춥니다.");
    }

}

위 코드에서 빨간색 부분이 당연히 에러가 납니다. 저런 클래스가 StoreManager에는 없기 때문입니다. 이 번들에서 다른 번들의 패키지를 import 하면 됩니다.

사용자 삽입 이미지

MENIFEST.MF 파일을 클릭하고, Dependecies 탭을 클릭하고 오른쪽의 imported Packages에서 add를 클릭하여, 노출된 패키지 중에서 검색을 합니다. service와 service.impl을 클릭하고 저장해 줍니다.

그런다음 Organize Import를 하면, 에러는 사라집니다.(위 과정을 거치지 않고, Organize Import를 했을 때, 노출된 패키지 중에서 검색 한 다음 자동으로 MENIFEST.MF 파일에 import-package를 추가해주고, 그 다음에 소스 코드에서도 import를 추가해주면 좋겠는데 말이죠...)


6. 새로운 번들 설치하기

위에서 만든 번들을 이전 번들과 마찬가지로, Export 해서 배포 가능한 번들 Jar로 만들어 주고, install 합니다.

사용자 삽입 이미지

7. 시작 합니다.

INSTALLED 상태가 됐네요. 이제 시작하기 전에 혹시나 이 번들이 필요로 하는 패키지를 못찾지는 않았을까 걱정이 되서, diag [id]로 확인해 봤습니다.

사용자 삽입 이미지

없는게 없다는군요. 그럼 start [id]로 시작시켜 봅니다.

사용자 삽입 이미지

귿!! (번들 번호가 바꼈는데, 제가 구현할 때 빠트린게 있어서 다시 Uninstall 하고, Export 한 다음에 다시 Install 해서 그렇습니다.)

다음 글에서는 이렇게 구현체를 직접 제공하는게 아니라, 인터페이스만 제공 하고 서비스 레지스트리를 사용해서 서비스로 노출하는 방법을 살펴보겠습니다.

================================================================================
복잡한 샘플

한번 ACTIVE 상태가 되면, 그 안에서 노출시킨 패키지의 클래스들 중에서 다른 번들에 의해 사용되는 클래스는 번들이 사라져도, 레지스트리에 계속 남아서 사용됩니다.

오묘한, 번들 라이프사이클과 서비스 레지스트리. 이 부분은 꼭 열공해야겠습니다.
top

Write a comment.


1. Equinox 기반 OSGi 번들 만들기

Spring DM/exercise : 2008.02.06 10:06


1. Eclipse를 다운로드 합니다.
http://www.eclipse.org/downloads/ 이 곳에서 Ecliipse Classic 3.3.1.1을 다운 받습니다.

2. Eclipse를 실행하고, 새 프러그인 프로젝트를 생성합니다.
Ctrl + N -> Plug 입력
사용자 삽입 이미지

3. 프로젝트 이름을 넣고, 맨 아래 Target Platform을 OSGi 프레임워크 Equinox로 선택합니다.

사용자 삽입 이미지

4. Next를 클릭합니다. 여기서 버전이 중요한데, OSGi에서는 같은 번들 여럿이 다른 버전으로 동시에 동작할 수 있습니다. A라는 번들이 B_1.0.0.jar 번들을 사용하면서, C라는 번들이 B_1.1.0.jar 번들을 사용할 수 있습니다. 그 상태에서 이 상태에서 A번들을 잠시 멈췄다가 B_1.0.0.jar 번들대신에 B_1.1.0.jar 번들을 사용하도록 수정한 다음에 다시 시작시키는 등의 컨트롤이 가능합니다.

또 하나 중요한 것은 Plug-in Options에 있는 Activator입니다. 이 녀석은 해당 번들을 시작시켜서 Active 상태가 되면 자동으로 콜백 메소드(start)를 호출하고, 멈출 때도 자동으로 콜백 메소드(stop)를 호출해 줍니다. 따라서 해당 번들을 가동시킬 때 어떤 일을 하고 싶다면, 이 옵션이 체크된 상태(기본)로 만들면 되고, 그렇치 않다면 체크를 없애고 넘어갑니다.

사용자 삽입 이미지

저는 일단 Activator로 만들겠습니다. 위 상태 그대로 Next를 클릭합니다.

5. 탬플릿을 선택합니다.

사용자 삽입 이미지

탬플릿을 선택하는 이유는 코드 탬플릿과 필요한 번들을 자동으로 추가해 주기 때문입니다. Bundle의 경우 org.osgi.framework를 추가해 주는데 그 이유는 이 번들이 가지고 있는 BundleActivator라는 인터페이스를 구현해야 하기 때문입니다. 이 인터페이스가 위에서 말한 콜백 메소드를 가지고 있습니다.

이제 Finish를 합니다. Next가 한번 더 있긴 하지만 별거 아닙니다. 궁금하신 분들은 눌러보셔도 됩니다.

6. 자 그러면 다음과 같이 프로젝트가 생성된 것을 볼 수 있습니다.

사용자 삽입 이미지
위에 보이는 화면은 MENIFEST.MF 파일을 비주얼하게 보여주는 편집기 입니다. 저곳의 하단에 있는 Depedency에서는 번들과 패키지 종속성을 설정할 수 있으며, Runtime에서는 외부로 노출 시킬 패키지를 설정할 수 있습니다.

일단 자동으로 만들어준 Activator부터 확인해보겠습니다.

7. Activator.java 파일을 열어 봅니다.

start와 stop 메소드에 간단한 메시지를 출력하도록 구현되어 있는 것을 볼 수 있습니다. 이 부분을 적당하게 수정해보겠습니다.

사용자 삽입 이미지

8. 이제 이 번들을 osgi 플랫폼 Equinox에서 실행해 보겠습니다.

Ctrl + 3 -> Run Dia 엔터  를 입력하여, Open Run Dialog 창을 띄우고, 왼쪽 맨 아래에 있는 OSGi Framework를 더블클릭합니다. 그러면 화면 중앙에 Equinox에서 가동시킬 모든 번들 목록이 표시 됩니다.

사용자 삽입 이미지

여기서 지금 제가 가동시키려는 것은 딱 하나, 맨 위에 있는 Store 뿐이니까 나머지는 모두 체크박스를 없애 줍니다. 아예 나타나지 않게 할 수도 있는데, Ctrl + 3 -> Target Plat 엔터를 입력하여, 이클립스가 로딩 될 때 읽어들인 번들 목록을 다음과 같이 모두 해제 시켜준 다음에 Apply를 클릭합니다.

사용자 삽입 이미지

자 그러면 프로젝트에서 에러가 납니다. 왜? org.eclise.osgi 번들까지 목록에서 제거했기 때문입니다. 그 녀석은 있어야 하니까(BundleActivator 때문에...) 그 녀석만 추가해 줍니다.

사용자 삽입 이미지

자 그러면 이제 에러도 없어지고, 정말 필요한 번들만 Target Platform에 선택해 두었습니다. 다시 Run Dialog로 돌아갑니다.

사용자 삽입 이미지

이제 Run을 클릭합니다.

9. 콘솔에 메시지가 출력된 것을 확인할 수 있습니다.

사용자 삽입 이미지
OSGi 플랫폼을 가동시키자 마자, Store 번들이 가동됐기 때문입니다.

ss 명력을 사용해서, 현재 이 플랫폼에 존재하는 번들들의 상태를 확인할 수 있습니다.

사용자 삽입 이미지

stop [id] 명령을 사용해서 id에 해당하는 번들을 멈출 수 있습니다.

사용자 삽입 이미지

bundle [id]를 입력하면 번들에 대한 구체적인 정보를 확이할 수 있습니다.

사용자 삽입 이미지

현재 이 번들은 등록 된 서비스도 없고, 사용하는 서비스도 없고, 공개한 패키지도 없고, 가져온 패키지가 하나 있습니다. org.osgi.framework

uninstall [id]를 입력하면, 해당 번들을 플랫폼에서 없앱니다.

사용자 삽입 이미지

install [url]  명령을 사용해서, 번들을 설치할 수 있습니다. 이렇게 가져오려면 일단 Export를 해서 배포 가능한 jar 파일로 만들어 주어야 합니다. 프로젝트를 우클릭하고, Export를 클릭합니다. 그리고 deployable 머시기를 선택합니다.

사용자 삽입 이미지

Next를 클릭하고, destination은 C:\으로 선택합니다.

사용자 삽입 이미지

Options에 가서 이 과정을 빌드 파일로 만들 수 있는데, 글이 너무 길어져서 생략하겠습니다. Finish 클릭하면 C:\plugins 폴더가 만들어지고, 그 안에 Store_1.0.0.jar 파일이 들어있는 것을 볼 수 있습니다.

다시 콘솔로 돌아와서

install file:c:/plugins/Store_1.0.0.jar

이렇게 입력하면 번들이 설치됩니다.

사용자 삽입 이미지

상태는 INSTALLED 상태이고, 이 상태에서 Start를 시켰을 때, 필요한 종속성이 모두 충족되면, ACTIVE 상태로 넘어갑니다. 필요한 종속성을 확인하려면, diag [id] 명령을 사용하면 됩니다.

사용자 삽입 이미지

이제 start [id] 명력을 사용해서 해당 번들을 가동하겠습니다.

사용자 삽입 이미지

1부 끝!!
top

Write a comment.