레이블이 OSGi&SpringDM인 게시물을 표시합니다. 모든 게시물 표시
레이블이 OSGi&SpringDM인 게시물을 표시합니다. 모든 게시물 표시

2010년 3월 10일 수요일

[03] 관련노트 셋

:: 참조문서 ::


★ OSGi 에서의 이벤트 시스템

OSGi 라이프사이클 레이어에서는 BundleEvent, FrameworkEvent를 서비스 레이터에서는 ServiceEvent라는 형태의 EventType객체를 정의하고 있다.

이벤트설명
BundleEvent번들의 Life Cycle 변경을 알리기 위한 이벤트
ServiceEvent서비스의 변경사항등을 알리기 위한 이벤트
FrameworkEventOSGi 프레임워크의 변경사항을 알리기 위한 이벤트

이 이벤트가 발생하면 번들컨텍스트에 각 타입에 대한 리스너를 등록할 수 있다. 이 이벤트들은 주로 OSGi의 동적인 환경을 이용하는 Extender를 개발할 때 주로 쓰이게 된다.

번들이 동적으로 설치되는 환경이라면, 설치할 때 각 번들마다 해줘야 할 일들을 Extender에 만들어 줌으로써 각 번들마다 각각 코딩할 필요가 없게 되어 중복된 코드를 줄이고 한 곳에서 관리할 수 있다.

※ Extender
Extender는 OSGi상에서 종종 사용되는 개념으로, 동적으로 설치되는 다른 번들/서비스의 설치/삭제 시 이벤트를 받아서 특정 동작을 수행하는 형태를 말한다. 특히 SpringDM이 Extender 개념을 아주 잘 활용한 예라고 볼 수 있다.

★ BundleEvent

BundleEvent는 번들의 라이프 사이클 변경을 알려주는 이벤트, BundleListener인터페이스를 구현한 객체를 생성하여 BundleContext.addBundleListener를 호출하여 등록한다.

public class MyListener implements BundleListener {
    public void bundleChanged(BundleEvent event) {
    }
}

또는 Activator에 BundleListener 인터페이스를 구현하면 간단히 핸들링 할 수 있다.

public class Activator implements BundleActivator, BundleListener {

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

    public void stop(BundleContext context) throws Exception {
        context.removeBundleListener(this);
    }

    public void bundleChanged(BundleEvent event) {
        ...
        ...
    }
}

번들을 설치할 때는 INSTALLED ▷ RESOLVED ▷ start() 메소드 실행 ▷ STARTED 순으로 이벤트가 진행된다. 번들을 삭제할 때는 stop() ▷ STOPPED ▷ RESOLVED ▷ UNINSTALLED 순으로 이벤트가 진행된다.

★ FrameworkEvent

FrameworkEvent는 프레임워크에서 일어나는 주요 이벤트로, BundleEvent와 마찬가지로 아래와 같이 FrameworkListener 인터페이스를 구현한 객체를 생성한 후 BundleContext.addFrameworkListener메소드를 호출하여 등록한다.

public class MyListener implements FrameworkListener {
    public void frameworkEvent(FrameworkEvent event) {
    }
}

FrameworkEvent는 일반적으로 잘 일어나지 않는 이벤트이며, 다음과 같은 이벤트가 존재한다.

번호이벤트설명
1STARTED프레임워크를 시작할 때 발생하는 이벤트
2ERROR프레임워크가 오류가 발생했을 때 던져주는 이벤트
3PACKAGES_REFRESHED각 번들이 임포트/익스포트하는 패키지가 리프레시되었을때 발생하는 이벤트
4STARTLEVEL_CHANGED각 번들의 ServiceLevel이 변경되었을 때마다 발생하는 이벤트
5WARNING번들에 관련하여 경고가 있을 때 발생

★ ServiceEvent

ServiceEvent는 서비스가 등록/수정/삭제되었을 때 알려주는 이벤트로 BundleEvent/FrameworkEvent와 마찬가지로 ServiceListener를 등록하면 프레임워크로부터 받을 수 있다.

public class MyListener implements ServiceListener {
    public void serviceChanged(ServiceEvent event) {
    }
}

이벤트설명
1REGISTERED서비스가 등록된 후에 발생하는 이벤트
2MODIFIED서비스가 변경된 후에 발생하는 이벤트
3UNREGISTERING서비스가 삭제되기 전에 발생하는 이벤트

초기 OSGi 설계당시에는 ServiceListener를 구현함으로써 각 번들은 자신이 원하는 서비스가 등록되었는지를 동적으로 알아내어 사용할 수 있었지만, 지금은 ServiceTracker를 이용하면 간단히 자신이 원하는 서비스에 대한 Tracking이 가능함으로 ServiceListener는 이제 별도 의미가 없어졌다.

★ OSGi 애플리케이션 이벤트

기존 자바에서 사용하던 일반적인 리스너 객체는 아래와 같은 방식이다.


그림1. 일반적인 리스너 객체

▶ 절차

1. 이벤트를 받고자 하는 Client가 이벤트 리스너를 생성해서 이벤트를 실제 생성하는 이벤트 객체에 자신을

   등록

2. 이벤트 소스는 OSGi 프레임워크가 되고, 작성한 번들의 액티베이터가 이벤트 리스너이다.

3. Add Listener 함수에서 이벤트 소스는 전달받은 이벤트 리스너들은 자신이 관리하는 리스너 객체 리스트

    에 추가한다.

4. 이벤트소스는 이벤트 발생시 이벤트 객체를 생성하고, 자신이 관리하는 리스너 리스트에 있는 모든 리스너

    객체들의 handleEvent 메소드를 호출하여 이벤트가 발생했음을 통보


위와 같은 구조는 자바에서 많이 사용하는 구조로 AWT와 같은 데에서는 매우 많이 쓰이고 있다. 하지만 OSGi 환경에서는 약간 부담이 되는 구조이다.

문제1. 각각의 이벤트와 리스너를 위해 클래스 또는 인터페이스를 작성해야 하는데, 클래스 개수가 늘어나는
         것은 관리상으로나 성능상으로나 문제가 있다.

문제2. 대부분의 경우 이벤트 리스너를 여러 개 관리(Stored Event Listeners)해야 하므로 이벤트 소스마다
         이를 저장하기 위한 컬렉션 개체가 하나씩 필요

문제3. 동적인 OSGi에서는 이벤트소스나 이벤트 리스너가 언제라도 없어질 수 있으므로, 이벤트 리스너 번들
         이 제거되었을 때 이벤트 소스에서 삭제하는 역할이 반드시 필요하며, 이벤트 소스는 매번 이벤트 리스
         너 존재여부를 체크해야 한다.


상기와 같은 문제로 OSGi에서는 주로 화이트보드 패턴을 이용한 이벤트 전달방식을 사용한다.



그림2. 화이트보드 패턴

화이트보드 패턴은 OSGi 프레임워크의 서비스 레지스트리를 이용하는 것으로, 각각의 이벤트 소스들에 이벤트 리스너를 등록하는 방식이 아니라 이벤트를 받고자 하는 번들이 이벤트 리스너를 서비스 레지스트리에 등록하고 이벤트 소스는 이벤트를 발생하고자 할 때, 서비스 레지스트리에서 이벤트를 받은 리스너들을 가져와서 이벤트를 던져주는 방식이다.

장점1. 이벤트 소스와 이벤트 리스너 사이의 직접적인 의존관계가 없다.

장점2. 이벤트 리스너가 모두 서비스 레지스트리에 등록되므로, 관계가 외부에 공개되며 통합적인 디버깅 툴
         의 지원이 가능하다.

장점3. 이벤트 리스너에 대한 다양한 테스트가 가능하여 개발작업이 용이하다.

장점4. 공개된 레지스트리를 이용하게 되므로, 이벤트소스나 이벤트 리스너 양측의 소스코드양이 줄어든다.

★ Event Admin 서비스

화이트보드 패턴은 쉽게 사용할 수 있지만 여전히 문제점이 남아 있다.

문제1. 이벤트 소스와 이벤트 리스너가 완벽히 분지되지 않는다. 즉, 이벤트 소스가 특정한 리스너, 특정한 이
         벤트 객체를 알고 있어야 한다는 것은 변함이 없다.

문제2. 서비스 레지스트리로부터 리스너 서비스를 얻어오고 트래킹하는 등의 코드를 이벤트 소스에 추가해야
         한다.

상기와 같은 문제점을 해결하고자 추가된 서비스가 OSGi Event Admin이다.




그림3. Event Admin 서비스

설명1. 각 번들은 Event Admin 서비스가 제공하는 EventHandler라는 인터페이스를 구현하여 이것을 서비스  
        레지스트리에 등록한다.

설명2. 이벤트 소스 번들에서 이벤트를 생성하여 Event Admin 서비스에게 Event를 보낸다.

설명3. Event Admin 서비스가 이벤트를 받은 시점에 등록된 이벤트 핸들러들의 스냅샷스냅샷을 만든다는 의미는, 이벤트를 받았을 떄 마치 사진을 찍듯이 등록된 이벤트 핸들러들의 리스트를 복사해두고 전달을 시작하고, 전달하는 작업 중간에 새로운 핸들러가 등록이 되더라도 그 핸들러는 현재 전달중인 이벤트를 받을 수 없다. 을 만들고 그 이벤트
         핸들러들에게 이벤트 객체를 전달한다.


상기와 같이 Event Admin 서비스를 구현한 Equinox 구현체는 org.osgi.service.event에 들어 있다.

★ Event Object


이벤트 객체는 다음과 같은 2개의 속성을 가진다.

1. Topic

이벤트 타입을 표시하기 위한 주제 속성. [주로 Event Listener들이 자신이 원하는 이벤트만 받고자 할 때 필터링시 사용]
- '/'로 분리된 Reverse Domain 형태의 계층 구조 스트링을 사용
- 일반적으로 fully/qualified/package/ClassName/ACTION 형태의 구조를 가진다.

2. Properties

이벤트에 대한 추가적인 속성을 가지는 컬렉션 개체.
다음은 주로 사용하는 속성들이며 이 상수에 대한 정의는 org.osgi.service.event.EventConstants에 정의


값의 형식키 이름 문자열속성 상수설명
BUNDLEbundleBundle해당 이벤트와 관련된 번들
BUNDLE_IDbuild.idLong번들의 ID
BUNDLE_SYMBOLINAMEbundle.symbolicNameStringManifest에 정의된 번들의 SymbolicName
EVENTeventObject이벤트 재전송을 위해 쓸 수 있는 현재 이벤트 객체 자체
EVENT_TOPICevent.topicsString[]이벤트가 해당하는 Topic 값, Event개체의 topic 변수와 중복되는 값이지만, 필터링할 때 사용할 수 있도록 Properties안에도 들어 있다.
EXCEPTIONexceptionThrowable발생한 Exception 또는 Error 개체
EXCEPTION_CLASSexception.classStringException 클래스 이름
EXCEPTION_MESSAGEexception.messageStringException.getMessage()에서 얻어지는 Exception 문자열
MESSAGEmessageString이벤트의 내용을 설명하는 문자열
SERVICEserviceServiceReference등록되거나 수정된 Service에 대한 ServiceReference
SERVICE_IDservice.idLongService의 ID
SERVICE_OBJECTCLASSservice.objectClassString[]서비스의 실제 객체인 objectClass의 이름배열
SERVICE_PIDservice.pidString프레임워크에서 부여한 Service의 Persistent ID값
TIMESTAMPtimestampLong이벤트가 일어난 시간

EventHandler를 등록할 때 EVENT_TOPIC과 EVENT_FILTER 두 가지 옵션을 통해 필터링이 가능하다.

1. EVENT_TOPIC

'*'을 지정하면 모든 이벤트를 받을 수 있으며 'org/osgi/framework/BundleEvent/*'과 같이 세부 항목에 '*'을 지정하면 그 패키지에 해당하는 모든 이벤트를 받을 수 있다. 중간에는 '*'이 올 수 없으며 배열형태로 원하는 이벤트만 지정하여 받을 수도 있다.

2. EVENT_FILTER

RFC2254 "The String Representation of LDAPLDAP(Lightweight Directory Access Protocol)은 TCP/IP 위에서 디렉토리 서비스를 조회하고 수정하는 응용프로토콜  Search Filters." 에 기반한 필터 문자열을 지정하여 원하는 속성의 이벤트만 필터링이 가능하다.

예를 들면 SymbolicName이 Google인 것만 : bundle.symbolicName=*.Google로 지정하면 되며 번들 id가 4이하 중에서, symbolic name에 eclipse인 것은 (&(bundle.id<=4)(bundle.symbolicName=*eclipse*))라고 하면 된다.

★ Event Admin에게 이벤트 보내기

Event Admin 서비스에게 이벤트를 전송하기 위해서는, 서비스 레지스트리로 부터 Event Admin 서비스를 가져와야 한다. 이를 위해서는 BundleContext.getServiceReference 함수나 4장에서 배운 ServiceTracker를 이용해서 손쉽게 가져와서 사용할 수 있다.

Event Admin의 이벤트 전송 함수는 sendEvent와 postEvent 두 가지가 있다.

1. sendEvent : 동기 전송

sendEvent를 호출할 때, 해당 Event를 받는 쪽에서 모든 처리를 할 동안 호출하는 쪽으로 리턴되지 않는다. 이 경우 받는쪽에 문제가 발생하면 호출하는 쪽까지 위험하므로 반드시 스레드 처리를 해주는 것이 좋다.

2. postEvent : 비동기 전송

postEvent를 호출할 때는 EventAdmin 내부에서 해당 Event를 받을 핸들러로 보내는 비동기처리가 되므로,
postEvent를 호출하는 쪽으로 바로 리턴된다.

2010년 3월 8일 월요일

[02] 관련노트 둘

:: 참조문서 ::
http://docs.google.com/Doc?docid=0AUlUhxYWZkIKZGZwNGRnMmdfN2NkcnE1cGRz&hl=en

★ SOA(Service Oriented Architecture)


SOA는 '서비스'와 '이를 조합하여 하나의 애플리케이션을 구축하는 것'이라고 볼수 있다. 웹서비스 기반의 SOA와는 다르게 OSGi는 현재로서는 하나의 JVM 인스턴스(한 대의 컴퓨터)안에서 구성한는 Java Object based SOA라고 볼 수 있다.




그림1. SOA 아키텍처

그림1과 같은 SOA구조를 Publish, Find & Bind 모델이라고도 하며 OSGi도 이 모델을 따르고 있다.

★ OSGi에서의서비스

OSGi에서는 이렇게 만들어진 서비스를 서비스 레지스트리라고 하는 곳에 등록함으로써 서로 다른 번들 간에 서비스를 이용할 수 있다. 여기서 서비스 레지스트리(Service Registry)는 서비스 중계자의 역할을 한다.

★ OSGi 서비스 등록과 해지

번들 컨텍스트 개체에는 다음과 같은 두가지 메소드가 있다.

1. public ServiceRegistration registerService(String clazz, Object service, Dictionary properties);

2. public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary
    properties);

위의 메소드를 통해 STARTING, ACTIVE, STOPPING 단계 중 아무 때나 서비스를 등록할 수 있다.
(STOPPING 단계에서 번들이 중단되면 자동으로 서비스가 해지되기 떄문에 별 의미가 없다.)

서비스 등록시에 필요한 3가지 인자

  • 해당 서비스 객체의 인터페이스
  • 인터페이스 구현 객체
  • 서비스 속성 Dictionary

3번째 인자인 Dictionary는 키/값의 쌍으로 된 컬렉션 개체를 지정한다. 모든 서비스는 service.id라는 값을 가지며 이 값은 프레임워크에 의해 자동으로 지정되어 변경될 수 없다.

서비스를 등록하면 각 서비스당 하나의 ServiceRegistration 객체를 가지며 이 객체는 등록된 서비스와 직접적으로 연결되는 인터페이스로 서비스에 대한 접근자인 serviceReference를 읽어오거나, Service의 등록을 해지할 때 사용된다. 여기서 주의 할것은 서비스 해지는 반드시 serviceReference를 통해서만 가능하다는 것이다. (이것은 서비스를 등록한 번들만이 해지할 수 있게 하기 위해서 이다.)

★ OSGi 서비스 사용하기

등록된 서비스는 serviceReference라는 객체로 참조된다. 이 객체는 서비스의 속성정보나 등록한 번들에 대한 객체, 사용 중인 번들의 정보를 읽어올 뿐 서비스객체는 얻어오지 못하지만, 아래 메소드를 이용하면 실제 서비스 객체를 얻어올 수 있다.

BundleContext객체의 getService() 함수

이와같이 번들컨텍스트를 통하여 서비스 객체를 얻는 이유는 프레임워크가 실제로 서비스를 사용 중인 객체에 대한 의존성 정보를 유지/관리하기 위함이다.

번들 사이에는 의존관계가 있을수 있는데, 이를 제대로 처리하지 않으면 우리가 의도한 결과가 나오지 않을 수도 있다. OSGi는 동적인 환경이므로, 내가 사용하려는 서비스가 원하는 시점에는 없다가, 그 시점 이후에 등록이 될 수도 있다. 즉, 사용하고자 하는 서비스에 동적으로 대응하도록 코딩이 필요하다.

★ OSGi 서비스 추적하기

서비스를 찾아와서 사용하는 부분은, 그 시점에 서비스가 존재하지 않을 수도 있기 때문에 다시 한 번 확인이 필요하다. 이와 같은 처리 부분은 start 메소드에 구현하기 보다는 작업용 스레드로 구현하는 것이 이롭다.

하지만 동적인 환경에서 서비스가 존재하는지를 체크하는 작업은 매우 빈번한 작업이므로 그때마다 스레드로 구현하는 것은 귀찮은 일이다. 이 작업을 좀 더 유연하게 처리해주기 위해 지원하는 서비스가 ServiceTracker이다. ServiceTracker는 OSGi R2부터 추가된 유틸리티 클래스이다. ServiceTracker의 구현체는 osgi.cmpn.jar파일 안에 들어 있다. ServiceTracker의 주요 메소드는 아래와 같다.

번호메소드설명
1ServiceTracker()생성자
2open(), close()사용전 반드시 open, 사용 후 close
3getService(), getServices()...
4getServiceReference(), getServiceReference()...
5waitForService(timeout)데드락 발생가능성 때문에 반드시 스레드로 작성해서 호출해야 한다.

★ 여러 개의 서비스 사용하기

서비스를 시작하는 순서는 기본값으로 OSGi가 0으로 설정함으로 일정하지 않다. 따라서 Run Configurations에서 Start Level를 정해서 정해진 순서 순으로 시작할 수 있도록 한다.

2010년 3월 6일 토요일

[01] 관련노트 하나


:: 관련 문서 ::





★ Bundle State (번들 상태정보)



그림1. 번들의 전체 라이프 사이클

번들의 상태정보는 그림1에서 처럼 총 6개의 상태정보를 가진다. Bundle.getState() 함수를 호출하면
아래 표와 값은 정보를 얻을 수 있다.

상태정보정의
Installedpublic static final int INSTALLED0x00000002
Resolvedpublic static final int RESOLVED
0x00000004
Uninstalledpublic static final int UNINSTALLED
0x00000001
Startingpublic static final int STARTING
0x00000008
Activepublic static final int ACTIVE
0x00000020
Stoppingpublic static final int STOPPING
0x00000010

표1. 번들 상태정보 상수 정의

1) INSTALLED 상태

OSGi 프레임워크는 번들 인스톨 요청이 들어오면 여러가지 오류가 있는지 확인 후 번들 설치를 완료한다.

2) RESOLVED 상태

ResolvingResolving은 번들 간의 익스포트/임포트를 연결해주는 것을 말한다.  프로세스에 의해 번들이 임포트하는 모든 패키지와 Required 번들이 OSGi 프레임워크상에 존재하는지를 검사한다. 모든 패키지를 성공적으로 찾으면 RESOLVED 상태가 된다.

번들을 인스톨하면 순간적으로 INSTALLED -> RESOLVED로 넘어간다. 만약 INSTALLED 상태로 있는 번들이 존재하면 문제가 있음을 짐작할 수 있으며 보다 상세한 정보를 보기 위해서는 커맨드 창에서 'diag #'을 입력한다.

3) STARTING 상태

RESOLVED 상태의 번들은 start 명령에 의해 시작될 수 있으며, start 명령을 받은 번들은 BundleActivator가 존재시 start이 함수는 OSGi에 의해 호출되는 번들 초기화 함수이다. 이 함수 안에서 예외가 발생하게 되면 번들은 다시 RESOLVED 상태로 돌아간다. 주의할것은 이 함수안에서 시간이 많이 걸리는 작업을 행하면 안된다.  메소드를 호출하여 Activation 단계를 시작한다.

4) ACTIVE 상태

Resolving 및 초기화가 성공적으로 완료되면 번들은 ACTIVE 상태가 된다.

5) STOPPING 상태

콘솔에서 stop 명령에 의해 STOPPING 상태로 들어가며, OSGi 프레임워크가 Activator에 있는 stop() 메소드를 호출한다. stop() 메소드 호출이 끝나면 다시 RESOLVED 상태로 돌아간다.

6) UNINSTALLED 상태

uninstall 명령을 통해 설치된 번들을 삭제할 수 있으며, UNINSTALLED 상태로 변경된 후 번들 정보가 바로 지워지기 때문에 이 상태의 번들을 보는 것은 불가능하다.

★ BundleContext

BundleContext는 OSGi 프레임워크와 번들을 연결해주는 객체로 OSGi상에서 현재 번들의 컨텍스트 정보를 제어하는 수단이다. 주요 기능은 아래 도표와 같다.

번호기능
1새로운 번들 설치 : installBundle(...)
2번들정보 얻어오기 : getBundle(...)
3서비스 등록하기 및 서비스 가져오기
4프레임워크 이벤트에 Subscribe 또는 Unsubscribe 하기
5영구저장소에 파일 생성하기 : getDataFile(...)
6시스템 프로퍼티 읽어오기 : getProperty(...)

★ 조각번들 (Fragment Bundle)

조각번들은 OSGi R4에서 새로 추가된 개념으로 특별한 형태의 OSGi 번들이다.  혼자서는 아무런 일도 하지 못하며, 심지어는 Activator가 없어서 start 할 수도 없다. 이 번들들은 Host 또는 Master 번들이라고 불리는 다른 번들에 붙어야만 동작되는 번들이다. 이런 작업들은 Attaching이라고 하며, Resolving하는 도중에 일어나는 작업 중의 하나이다.

이런 조각번들은 다국어 지원 애플리케이션을 작성할 때 각 언어별 파일을 따로 작성해서 호스트 애플리케이션에 붙여서 사용하는 용도로 사용된다.

2010년 3월 3일 수요일

[소개] OSGi & SpringDM

저자 : 권장혁
출판사 : 위키북스 ISBN : 978-89-92939-28-7
가격 : ₩25,000

[목차]

펼쳐두기..