정보공간_1

[2기 강북 송석호] OSGi FrameWork에서 Bundle 만들기 본문

IT 놀이터/Elite Member Tech & Talk

[2기 강북 송석호] OSGi FrameWork에서 Bundle 만들기

알 수 없는 사용자 2012. 9. 20. 03:00

안녕하세요.

강북 멤버십 20-2 송석호입니다.

이번 달에는 equinox 기반으로 OSGi 번들을 구현 해보도록 하겠습니다.


1. My Bundle 만들기

시작하기 전에, 실행하기 위한 OSGi 프레임워크를 필요로 합니다. 저번에 설명 드렸던 세 개의 오픈소스 ( Equinox, Apache Felix , Knopflerfish)구현 들이 있습니다. 어떤 것을 선택하든 작성할 코드는 똑같겠지만 실행 방법은 다를 수 있습니다. 여기서는 Equinox 를 사용할 것이며, Equinox는 Eclipse 자체가 사용하는 런타임입니다. 설치된 Eclipse 가 있다면 거기서 복사하실 수 있습니다.

org.eclipse.osgi_3.7.2.v20120110-1415.jar 파일을 찾고 OSGi를 실행할 디렉토리에 복사하기만 하면 됩니다. Eclipse 설치본이 없다면, http://download.eclipse.org/eclipse/equinox/ 에서 Jar 파일을 다운로드 할 수 있습니다. 개발디렉토리에서 커맨드창을 열고 아래의 커맨드를 실행합니다.

> java -jar org.eclipse.osgi_3.2.1.R32x_v20060919.jar -console

실행한 후에는 osgi> 프롬프트가 보이면 OSGi가 실행 된 것입니다.
osgi> 프롬프트를 통해 Equinox에 원하는 명령을 실행 할 수 있습니다. 우선 help 를 입력해서 명령 리스트를 보고, 그중에서 ss 를 명령어는 자주 사용하는 명령입니다. 이것은 “Short Status : 간략한 상태보기” 를 의미하며, 우리에게 설치된 번들의 리스트와 현재 어떤 상태인지를 보여줍니다. 
( “번들:Bundle”은 OSGi 개념에서의 모듈입니다. Eclipse로 개발을 해 보셨다면 이것이 플러그인 이라고 알 수도 있습니다. 기본적으로 번들과 플러그인은 같은 것입니다. )
"ss" 명령어를 입력하면 Equinox 는 다음과 같은 내용을 출력합니다.
osgi> ss
Framework is launched.
id State       Bundle
0 ACTIVE      org.eclipse.osgi_3.7.2.v20120110-1415

현재 단 하나의 번들이 설치되어있고, 실행중인 상태(Active)이며 그것이 시스템 번들이라는 것입니다.  OSGi 번들은 OSGi 에서 특수한 번들이며 항상 존재하고, 프레임워크 자신을 나타냅니다.

새로운 번들을 만들어 보기 위해 같은 디렉토리에  MyBundleActivator.java 라는 파일을 만들고 아래의 코드를 작성합니다 .

 MyBundleActivator.java

---------------------------------------------------------------------------------------------------------------

import org.osgi.framework.*; public class MyBundle implements BundleActivator { public void start(BundleContext context) { System.out.println("MyBundle Start"); } public void stop(BundleContext context) { System.out.println("MyBundle Stop"); } }

---------------------------------------------------------------------------------------------------------------

번들은 자신의 이름,버전과 같은 다양한 정보를 가지고 있는 Manifest 파일을 필요로 합니다.  MyBundle .mf 라는 파일을 만들고 아래의 텍스트를 작성합니다. 이 파일이 Manifest 파일 마지막 줄에는 빈 줄을 추가해주세요. 

 MyBundle.mf

---------------------------------------------------------------------------------------------------------------

Manifest-Version: 1.0 Bundle-Name: MyBundle Bundle-Activator: MyBundle Bundle-SymbolicName: MyBundle Bundle-Version: 1.0.0 Import-Package: org.osgi.framework

---------------------------------------------------------------------------------------------------------------

새로운 커맨드 창을 열고 ( 실행된 OSGi를 그대로 내버려 두기 위해서 ) javac 명령어 및 jar 명령어를 이용하여 Jar 파일을 만듭니다.

> javac -classpath org.eclipse.osgi_3.7.2.v20120110-1415.jar MyBundle.java

> jar -cfm MyBundle.jar MyBundle.mf MyBundleActivator.class

그리고 OSGi가 실행되어 있는 콘솔에서 install file 명령어로 번들을 실행합니다.

osgi> install file:MyBundle.jar

Bundle id is 1

다시 ss 를 입력하면 아래와 같은 내용을 보실 수 있습니다.

osgi> ss

Framework is launched.

id State Bundle 0 ACTIVE system.bundle_3.2.1.R32x_v20060919 1 INSTALLED MyBundle_1.0.0

MyBundle번들이 설치되었지만 아직 실행상태(Active)는 아니기 때문에 번들을 실행상태로 만들기 위해 start 1 을 입력해서 번들을 시작하도록 합니다. “1″ 은 첫 번째 컬럼에 나와있는 번들의 ID 입니다. 이렇게 입력하면 "MyBundle Start" 라는 메시지를 볼 수 있고, 다시 stop 1 을 입력하면"MyBundle Stop" 를 볼 수 있습니다.

osgi> start 1

MyBundle Start

osgi> stop 1

MyBundle Stop

여기서 코드는 BundleActivator 인터페이스를 구현하고, 프레임워크에게 우리한테 중요한 LifeCycle 이벤트를 알릴 수 있도록 하였습니다. 번들이 시작되면, 프레임워크는 start()메소드를 호출하고, 번들이 멈출 때 stop()메소드를 호출하여 줍니다. 이런 일 들은 Manifest 파일에 있는 "Bundle-Activator: MyBundleActivator" 에 의해 가능해 집니다.


2. FrameWork와 연동하기

MyBundle을 살펴보면 시작과 종료 시에 메세지를 출력하는 것이 Bundle Activator 인터페이스를 구현하여 start 와 stop 메소드를 제공함으로서 가능했습니다. start와 stop 메소드는 BundleContext를 인자로 받습니다. 그렇다면 BundleContext이 무엇을 할 수 있는지 알아보도록 합시다.

책의 내용을 빌리면 BundleContext는 OSGi 프레임워크가 우리에게 보내주는 매직티켓이라고 합니다. 코드상에서 프레임워크와 연관된 작업을 해야할 필요가 있을 때 BundleContext 를 사용하게 될것입니다. 실제로 이것은 OSGi API 와 연결하기 위한 방법이며 프레임워크는 번들이 시작될때 BundleActivator 를 통해 각 번들에게 이 티켓중의 하나를 발권하게 된다고 합니다.

이번에는 OSGi API를 통해 MyBundle을 찾아내고 Uninstall하는 번들을 만들어 봅시다.MyBundleKiller.java 라는 파일을 만들고 아래의 코드를 작성합니다.

 MyBundleKiller.java

---------------------------------------------------------------------------------------------------------------

import org.osgi.framework.*;   public class MyBundleKiller implements BundleActivator { public void start(BundleContext context) { System.out.println("MyBundleKiller searching..."); Bundle[] bundles = context.getBundles(); for(int i=0; i < bundles.length; i++) { if("MyBundle".equals(bundles[i].getSymbolicName())) { try { System.out.println("MyBundle found, " + "destroying!"); bundles[i].uninstall(); return; } catch (BundleException e) { System.err.println("Failed: " + e.getMessage()); } } } System.out.println("MyBundle bundle not found"); } public void stop(BundleContext context) { System.out.println("MyBundleKiller shutting down"); } }

---------------------------------------------------------------------------------------------------------------

이제 Manifest 를 작성합니다. 역시 마지막줄에 빈줄이 있어야 하는것은 역시 매우 중요합니다. 아래 내용을 HelloWorldKiller.mf 에 작성합니다.

 MyBundleKiller.mf

---------------------------------------------------------------------------------------------------------------

Manifest-Version: 1.0 Bundle-Name: MyBundleKiller Bundle-Activator: MyBundleKiller Bundle-SymbolicName: MyBundleKiller Bundle-Version: 1.0.0 Import-Package: org.osgi.framework

---------------------------------------------------------------------------------------------------------------

컴파일 하고 Jar 파일을 만듭니다.

> javac -classpath equinox.jar HelloWorldKiller.java
> jar -cfm HelloWorldKiller.jar HelloWorldKiller.mf HelloWorldKiller.class

다시 OSGi 콘솔로 번들을 설치하고 상태 리스트를 확인해 봅니다.

osgi> install file:MyBundleKiller.jar

osgi> ss

Framework is launched.

id State Bundle 0 ACTIVE system.bundle_3.2.1.R32x_v20060919 1 ACTIVE MyBundle_1.0.0 2 INSTALLED MyBundleKiller_1.0.0

MyBundle Killer 를 실행시키면. 아래와 같은 출력을 보실수 있습니다.

osgi> start 2

MyBundleKiller searching...
MyBundle found, destroying!
MyBundle Stop

마지막 출력라인은 원본 MyBundle 번들에서 나왔다는것을 알수 있다. 우리가 Killer 를 실행하기 전에 그 번들이 active 상태였기 때문에 번들을 제거하기 전에 번들이 stop 상태가 된 후 제거가 된 것입니다.

다시 상태 리스트를 보면 MyBundle 번들이 사라졌습니다.

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      system.bundle_3.2.1.R32x_v20060919
2       ACTIVE      MyBundleKiller_1.0.0

지금 까지 구현된 내용만 보면 어떤 번들이라도 다른 어떤 번들을 제거할 수 있는것처럼 보인다. 하지만 OSGi 는 프레임워크와의 모든 연계작업들을 세밀하게 제어하는 종합적인 보안계층을 가지고 있기 때문에 특정 “관리” 번들에 의해서만 번들이 제거될수 있도록 구현 할수도 있습니다. 

3. Bundle간 의존 관계

번들(Bundles)은 모듈(Modules)입니다. 번들은 우리의 단일 프로젝트를 관리가 가능한 조각들로 분리하여 OSGi 런타임 안으로 각각 로드될수 있도록 해줍니다. 모듈들이 다른 모듈에 의존관계(Dependency)를 가지고 있다는 겁니다. 평범한 Jar 파일에서는 다른 Jar 파일들에 대해 신뢰할수 있게 의존관계를 명시하는 일이 불가능했습니다. ( Manifest 에 있는 Class-Path 항목은 신뢰할 만한 방법이 아닙니다. ) 그래서 Jar 파일안에 있는 코드가 정말로 동작할지 아니면 런타임시에 ClassNotFoundException 을 낼지는 알 수 없습니다.

OSGi 는 이 문제를 좋은 방법으로 해결했습니다. 코드로 직접 보여드리면서 설명하겠습니다

osgitut/movies/Movie.java

 파일을 만들고 아래 코드를 작성합니다.

Movie.java

---------------------------------------------------------------------------------------------------------------

package osgitut.movies;
 
public class Movie {
    private final String title;
    private final String director;

    public Movie(String title, String director) {
        this.title = title; this.director = director;
    }
    public String getTitle() { return title; }
    public String getDirector() { return director; }
}
---------------------------------------------------------------------------------------------------------------

같은 패키지 안에 인터페이스를 만들기위해 osgitut/movies/MovieFinder.java 파일을 만들고 아래코드를 작성합니다. 

MovieFinder.java

---------------------------------------------------------------------------------------------------------------

package osgitut.movies;
 
public interface MovieFinder {
    Movie[] findAll();
}
---------------------------------------------------------------------------------------------------------------

이제 이 두개의 클래스들을 번들안에 집어 넣습니다. 전과 같이 Manifest 파일을 만들어야 하므로, MoviesInterface.mf 를 만들고 아래와 같이 작성합니다.

 MoviesInterface.mf

---------------------------------------------------------------------------------------------------------------

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Movies Interface
Bundle-SymbolicName: MoviesInterface
Bundle-Version: 1.0.0
Export-Package: osgitut.movies;version="1.0.0" 
--------------------------------------------------------------------------------------------------------------- 이전에는 보지 못했던 Export-Package 라는 새로운 줄이 있습니다. 이는 번들에서 osgitut.movies 패키지가 export 되었다는것을 의미합니다. 예전 Java jar 파일에서는 모든 것이 export 되었으므로, 처음에는 좀 이상하게 보일수 있습니다.  만약 번들안에 있는 패키지가 Export-Package 헤더에 포함되지 않는다면, 그 패키지는 오직 당신 모듈안에서만 접근이 가능합니다. 또한 패키지에 버전 번호를 붙힌것을 볼수 있습니다. 이것은 중요한 것입니다. 하지만 제공하지 않으면 OSGi 는 자동적으로 버전 “0.0.0″을 패키지에 할당하게 됩니다. 

이제 이 번들을 빌드해 봅시다.

> javac osgitut/movies/Movie.java osgitut/movies/MovieFinder.java

> jar -cfm MoviesInterface.jar MoviesInterface.mf osgitut/movies/*.class

번들을 런타임에 설치하진 않고 번들에 의존하는 다른 번들을 만들 것입니다. MovieFinder 인터페이스의 구현부를 작성할 것이므로 , 아래를 osgitut/movies/impl/BasicMovieFinderImpl.java 에 작성합니다.

MovieFinderImpl.java

---------------------------------------------------------------------------------------------------------------

package osgitut.movies.impl;
 
import osgitut.movies.*;
 
public class BasicMovieFinderImpl implements MovieFinder {

  private static final Movie[] MOVIES = new Movie[] {
    new Movie("The Godfather", "Francis Ford Coppola"),
    new Movie("Spirited Away", "Hayao Miyazaki")
  }; 
  public Movie[] findAll() { return MOVIES; }
}
---------------------------------------------------------------------------------------------------------------
매니페스트 파일 BasicMovieFinder.mf 를 아래와 같이 작성합니다.

 BasicMovieFinder.mf

---------------------------------------------------------------------------------------------------------------

Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Basic Movie Finder Bundle-SymbolicName: BasicMovieFinder Bundle-Version: 1.0.0 Import-Package: osgitut.movies;version="1.0.0" ---------------------------------------------------------------------------------------------------------------

다른 번들에 의해 Export 되었던 osgitut.movies 번들을 import 하고 있습니다.

2번째 번들을 컴파일하고 번들로 만들어 봅니다.
> javac -classpath MoviesInterface.jar osgitut/movies/impl/BasicMovieFinderImpl.java

> jar -cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class

이제, 이 번들들을 Equinox 에서 실행해 봅니다. 이제 익숙해 지셨을테니, 이번엔 상세한 설명은 하지 않겠습니다. 먼저 BasicMovieFinder 번들을 설치한후, ss 를 입력해 보세요. 번들이 INSTALLED 상태에 있다는것을 보실수 있습니다.

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0

INSTALLED 는 프레임워크가 번들을 설치했다는것을 의미하며, 아직 번들의 의존관계를 해결하지 못햇다는 것(not resolved)입니다. Equinox 에게 우리 번들에게 의존관계를 해결하도록 하는 하나의 방법은 refresh 명령입니다. refresh 4 를 입력하고 다시 상태 리스트를 확인해 봅니다.

osgi> refresh 4

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0

여전히 번들이 resolve 되지 않았습니다. Movie 클래스와 MovieFinder 인터페이스를 가지고 있는 번들을 설치해야만 합니다. 이것이 정말 문제인지를 확인하기 위해 diag 4 를 입력해서, 진단 정보를 불러봅니다.

osgi> diag 4

file:BasicMovieFinder.jar [4] Missing imported package osgitut.movies_[1.0.0,2.0.0).

진단 결과에 나왔듯이 osgitut.movies 패키지를 export 하고 있지 않기 때문에 import 가 불가능합니다.  이제 MovieInterface.jar를 설치하고 상태 리스트를 보면 리스트는 아래와 같습니다.

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       INSTALLED   BasicMovieFinder_1.0.0
5       INSTALLED   MoviesInterface_1.0.0

마지막으로 Equinox 에게 다시 BasicMovieFinder 번들에게 resolve 를 시도하도록 refresh 4 를 입력해 보는것입니다 다시 상태 리스트는 아래와 같습니다.

osgi> refresh 4

osgi> ss

Framework is launched.

id      State       Bundle
0       ACTIVE      org.eclipse.osgi_3.3.0.v20070208
4       RESOLVED    BasicMovieFinder_1.0.0
5       RESOLVED    MoviesInterface_1.0.0

BasicMovieFinder 이 이제 RESOLVED 가 되었습니다. 이것은 필수단계로서, 번들이 RESOLVED 상태가 되지 않으면 시작이 불가능하며, 또한 다른 번들들에게 Dependency 를 제공하지도 못합니다.참고로, 보통은 이런 방법의 수작업은 필요치 않습니다. 일반적으로 번들은 그들이 필요로 할때 자동적으로resolve 됩니다. 예를 들어 우리가 refresh 를 호출하지 않았어도 MoviesInteface 번들이 RESOLVED 상태가 된 것으로 알 수 있습니다.

4. Reference 
§ OSGi Service Platform Core Specification Release 4, Version 4.3”, The OSGi Alliance, Apr. 2011.
§ Song J.H., “Design and Implementation of Management Bundle for OSGi Framework”, Soongsil Univ., 2008.
§ 권정혁, “실전 OSGi & Spring DM”, 위키북스, 2009.
§ Guru`s Blog, http://xguru.net/
§ OSGi Alliance site, www.osgi.org 
§ Open OSGi middleware project site, www.knopflerfish.org 

5. finish 

OSGi Framework에서 번들을 만드는 것을 시작으로 각 번들의 의존관계를 파악하는 것을 해보았습니다.좀 더 내부적으로 이해하기 위해서 콘솔환경에서 작업해 보았습니다. 번들을 구현해보면서 OSGi에서 번들의 역활을 이해해 보셨으면 좋겠습니다. 좀 더 도움이 필요하다면 OSGi Release 4 API 에 대한 JavaDoc 을 참고하세요.