2005년 2월 17일 목요일

[펌] [강좌] OSGi로 여는 임베디드 자바 세상

]

 

OSGi로 여는 임베디드 자바 세상

 

임베디드 시스템 프로그래밍을 위한 최적의 개발언어와 도구는 무엇일까? 임베디드 시스템은 자바의 고향이다. 이제 다시 고향으로 돌아가려는 자바의 발걸음은 과연 금의환향이 될 수 있을까? OSGi는 임베디드 영역에서의 자바의 가능성에 대한 조심스러운 낙관을 가능하게 한다. 특집 4부에서는 OSGi를 통해, 자바의 플랫폼 독립성과 코드의 네트워크 이동성을 최대한 활용한 임베디드 자바 프로그래밍의 세계를 소개하고자 한다.

 

¨        배준현 (오비츠 무선인터넷 사업부)

 

도심 속에서 먼 산을 바라보면, 산은 언제나 그 자리에 멈춰 있는 것처럼 보인다. 하지만, 지구 밖에서 그 산을 본다면, 산은 지구의 자전 궤도를 따라 빠른 속도로 움직이고 있을 것이다. 미시적인 관점에서도 마찬가지다. 책상 위에 놓인 유리컵 속의 물은 정지해 있는 것처럼 보인다. 그러나 우리는 그 속에서 수많은 분자들이 끊임없이 운동하고 있음을 이미 알고 있다. 이 세상에 존재하는 모든 것은 움직이다. 심지어는 사랑까지도 움직이는 것이라고 하지 않는가.

 

필자는 임베디드 시스템에서의 이동성(mobility)이라는 패러다임은 두 가지 측면에서 아주 중요한 의미를 가지고 있다고 생각한다. 하나는 단말기의 이동성이며, 다른 하나는 서비스의 이동성이다. 예를 들면, PDA나 스마트폰과 같은 정보단말은 휴대가 간편하다는 측면에서 단말기의 이동성에 의미를 부여할 수 있는 임베디드 시스템이라고 할 수 있을 것이다. 그러나, 정보단말과 인터넷 정보가전을 비롯한 대부분의 임베디드 시스템은 메모리 공간이 매우 한정적이다. 그러므로, 동적인 서비스의 설치와 삭제는 거의 필수 불가결한 요소다. 그런 관점에서 서비스의 이동성은 임베디드 시스템의 영역에서 더 큰 의미를 지닌다고 볼 수 있다.

 

OSGi(Open Service Gateway initiative) 규격은 자바의 플랫폼 독립성과 실행코드의 네트워크 이동성을 이용하여 소용량 메모리 디바이스를 위한 동적인 서비스의 제공을 목표로 제정된 표준이다. 특집 4부에서는 J2ME(Java 2 Micro Edition) 중심의 임베디드 시스템을 위한 자바 기술의 동향과 함께, 홈 게이트웨이의 서비스 플랫폼으로서 주목을 받고 있는 OSGi 규격에 대해서 살펴볼 것이다.

 

1. J2ME라는 이름의 정글

 

J2ME란 무엇인가? 이 질문에 대한 대답은 간단하다. J2ME는 소비자 가전과 임베디드 디바이스를 위한 자바 2 플랫폼, 다시 말해, 포스트 PC 시대의 컴퓨팅 환경을 위한 자바 기술의 집합이라고 볼 수 있다. 그렇다면 그 집합의 경계선은 어디인가? 고민은 여기서부터 시작된다.

 

J2ME를 이루는 수많은 명세서들은 마치 정글처럼 복잡하게 구성되어 있다. 그리고, 자바카드(JavaCard)와 지니(Jini), 자바 임베디드 서버(Java Embedded Server) 등은 그 경계선이 애매한 위치에 존재한다. 그래서, 어디까지가 J2ME 영역이라고 확실하게 선을 그을 수 있는 방법은 없다. 어떤 사람은 썬이 배포한 Developer Essentials Java 2 Micro Edition이라는 CD에 포함된 것들을 J2ME 영역으로 보자는 해법을 제시하기도 했지만, 별로 만족스럽지 않다. 필자의 견해로는 JSR(Java Specification Request)의 섹션 2의 2.2절에서 명시한, 제안하고자 하는 명세서의 영역을 J2ME라고 밝힌 명세들을 J2ME 명세라고 보는 게 좋을 듯 하다. 하지만, 이것도 부족한 면이 있다. 그래서, 위에서 말한 두 견해를 적절히 조합해 보면 다음과 같은 명세들이 J2ME 영역에 포함되는 자바 기술이라고 할 수 있을 것이다.

 

u       PersonalJava

u       KVM(K Virtual Machine)

u       CLDC(Connected Limited Device Configuration)

u       CDC(Connected Device Configuration)

u       MIDP(Mobile Information Device Profile)

u       PDA Profile

u       Personal Profile

u       Foundation Profile

u       RMI Profile

u       Jini Technology

u       JavaCard Technology

u       Java Embedded Server

u       JavaPhone API

u       JavaTV API

u       JavaCar Profile

 

여기에서 언급한 자바 기술은 대부분 본지를 통해 여러 번 언급된 바 있거나, 아직은 특별히 주목해야 할 만큼 충분한 이슈를 가지지 못한 상태다. 그러므로 각각의 명세에 대해 따로 살펴보는 일은 불필요한 것으로 생각한다. 그러므로, 앞으로 살펴보게 될 OSGi를 위한 자바 플랫폼 환경으로서의 퍼스널 자바와, OSGi와 호환되는 썬의 홈 서버 게이트웨이 제품인 자바 임베디드 서버에 대해서만 살펴보도록 하자. 

 

2. 퍼스널 자바 = CDC + 퍼스널 프로파일

 

J2ME가 등장하기 전에는 임베디드 시스템을 위한 자바 기술로 임베디드 자바(eJava)와 퍼스널 자바(pJava)가 있었다. pJava는 주로 웹에 연결된 소비자 가전을 목적으로 한 자바 플랫폼이고, eJava는 특정 시스템을 제어하는 것이 목적이었다.

 

eJava와 pJava의 가장 큰 차이점은 코드의 네트워크 이동성의 유무다. 즉, pJava는 애플릿을 네트워크로 다운로드하여 실행하는 것을 주목적으로 하는 반면, eJava는 애플릿을 지원하지 않으므로 네트워크로부터 애플릿을 다운로드할 수 없다. 이런 차이로 인해 pJava의 명세는 CDC와 퍼스널 프로파일로 계승되고 있지만, eJava는 현재 J2ME에서는 전혀 거론되지 않고 사양길로 접어 들었다. (pJava와 eJava의 상세한 내용에 대해서는 본지의 98년 8월호와 99년 2월호 참조)

 

현재, 퍼스널 자바 응용 환경(PersonalJava Application Environment, 이하 PJAE)은 1.2 명세까지 발표되었고(물론, 향후에는 CDC와 퍼스널 프로파일로 발표될 것이다.), PJAE 1.2 환경은 JDK 1.1.8을 기반으로 하고 있다. 그리고, 1.2에서 가장 주목할 만한 변화는 JDK 1.2의 보안관련 API들을 대거 추가하였다는 점이다. 이것은 OSGi와 같은 동적인 서비스의 설치 및 삭제를 위해서 반드시 필요한, 견고한 보안 모델의 필요성을 충족시킨다는 점에서 아주 중요한 의미가 있다.

 

pJava용 애플리케이션의 개발은 두 가지의 접근방법이 있다. 그 중 하나는 퍼스널 자바 에뮬레이션 환경(PersonalJava Emulation Environment, 이하 PJEE)를 통해 pJava용 애플리케이션을 개발하는 것이고, 다른 한 가지는 작성한 애플리케이션의 PJAE 환경에서 수행될 수 있는 지를 자바체크(JavaCheck) 유틸리티를 통해 검사하는 것이다. 자바체크 유틸리티는 클래스 의존성을 검사하여 사용된 클래스들이 퍼스널 자바에서 사용할 수 있는 가에 대한 정보를 분석해 준다.

 

2.1 PJEE 활용

 

pJava의 애플리케이션 개발환경은 사용할 수 있는 API가 제한적이라는 점에만 유의한다면, 기존의 자바 개발환경과 거의 동일하다. PJEE를 활용한 pJava 응용을 개발하는 과정을 살펴보자.

 

       PJEE 다운로드 및 설치

다음 사이트에서 PJEE 3.1을 다운로드한다. (PJEE 3.1은 PJAE 1.2에 호환되는 에뮬레이션 환경이다.)

http://java.sun.com/products/personaljava/pj-emulation.html

PJEE에는 두 가지의 룩앤필이 있다. 하나는 테스트 플랫폼 즉, 솔라리스와 윈도즈의 네이티브 룩앤필이고, 다른 하나는 터처블(touchable) 룩앤필이다. 여기서는 Win32 룩앤필을 기준으로 한다.

 

       환경변수 설정

다음과 같이 PATH, JAVA_HOME, CLASSPATH 환경변수를 지정한다.
prompt> set PATH = %PATH%;c:\pjee3.1
prompt> set JAVA_HOME=c:\pjee3.1
prompt> set CLASSPATH = 
            .;c:\pjee3.1\lib\classes.zip;c:\pjee3.1\lib\classes_g.zip

       pJava 기반 소스 작성

다음과 같이 pJava 기반의 애플리케이션을 작성한다.

public class HelloWorld {

    public static void main(String[] args) {

        System.out.println("Hello, pJava");

    }

}

 

       컴파일 및 실행한다.
컴파일은 javac 컴파일러로 컴파일하지만, 실행은 PJEE의 pjava 명령어로 실행하도록 한다.
prompt> javac HelloWorld.java
prompt> pjava HelloWorld

       애플릿의 작성
다음과 같은 pJava용 애플릿을 작성하고 컴파일한다.

import java.applet.Applet;

import java.awt.Graphics;

 

public class HelloApplet extends Applet {

    public void paint(Graphics g) {

        g.drawString("Hello pJava Applet!", 50, 25);

    }

}

 

이 애플릿을 위한 HTML 코드는 다음과 같다.

<HTML>

<HEAD>

<TITLE>PersonalJava Test Page</TITLE>

</HEAD>

<BODY>

Here comes pJava Applet:

<APPLET CODE="HelloApplet.class" WIDTH=200 HEIGHT=25>

</APPLET>

</BODY>

</HTML>

 

       애플릿 실행

pJava용 애플릿의 실행은 pappletviewer를 통해 실행한다.

prompt>pappletviewer hello.html

 

2.2 JavaCheck 활용

 

자바체크는 클래스 파일을 분석하여 각 클래스의 의존성을 검색한다. 처음에는 먼저 코드 내의 의존성을 검색하고, 만일 코드 내에서 사용한 패키지, 클래스 또는 메쏘드의 위치가 파악되지 않으면 플랫폼 명세 파일(platform specification file)을 검색한다. 다시 말해, 코드 내에서 사용된 패키지, 클래스, 메쏘드가 사용자가 정의한 것이 아니라면, 플랫폼 명세 파일에 포함된 클래스임을 검색해 보는 것이다. 이 검색과정에서 모든 의존성이 결정(resolve)되지 않으면 자바체크는 발견된 문제에 대한 정보를 리턴한다.

 

 

여기에서는 다음 예제 코드를 자바체크로 의존성 검사를 하는 과정을 살펴보자.

 

import java.awt.*;

 

public class JCTest {

            

    public static void main(String[] args) {

        Frame f = new Frame("JavaCheck Test");

        f.pack();

        f.setSize(300, 300);

        f.show();

    }

}

 

       자바체크 다운로드 및 설치

 

다음 사이트에서 JavaCheck 3.0과 플랫폼 명세 파일을 다운로드하여 설치한다. http://java.sun.com/products/personaljava/javacheck.html

 

이 때, 주의할 점은 javacheckinstall.zip 파일과 javacheckspecinstall.zip 파일을 zip 유틸리티로 압축을 해제하는 것보다는 다음과 같이 인스톨러를 사용하는 것이 좋다는 것이다.

 

prompt> java cp javacheckinstall.zip JavaCheckInstaller

prompt> java cp javacheckspecinstall.zip JavaCheckSpecInstaller

 

       자바 소스 코드 컴파일

 

위에서 작성한 JCTest.java 파일을 컴파일한다.

 

prompt> javac JCTest.java

 

       JavaCheckV 실행

 

JavaCheckV는 간단한 텍스트 기반의 UI 툴이다. 다음은 JavaCheckV 유틸리티를 실행시키는 커맨드다.

prompt> java cp c:\javacheck\bin\JavaCheck.jar JavaCheckV

 

       플랫폼 명세 파일 선택

 

<화면 1>은 JavaCheckV 내에서 플랫폼 명세 파일을 선택하는 화면이다. JavaCheck 3.0에서는 specs 디렉토리 내에 pJava_1.1.0.spc 파일을 제공하고 있다. 이 파일을 선택하고 Add all 버튼을 누르면 사용할 플랫폼 명세 파일이 지정된다. JavaCheck 3.0에서는 1.1 명세를 지원한다. JavaCheck 3.1에서는 1.2 명세를 지원하는 플랫폼 명세 파일이 나올 예정이다.

 

<화면 1> 플랫폼 명세 파일 선택 화면

 

       클래스 패스 설정

 

<화면 2>는 클래스 파일의 위치를 지정하는 화면이다. 작성한 클래스 파일이 있는 디렉토리를 선택하고, Add all 버튼을 선택하면 검사할 클래스 파일들이 지정된다. 여기서는 앞에서 작성한 JCTest.class 파일을 선택하도록 하자.

 

<화면 2> 클래스 패스 지정 화면

 

       결과 분석

 

플랫폼 명세 파일의 선택과 클래스 패스의 지정이 완료되었다면 이제 검사를 시작하도록 하자. <화면 3> JCTest.class 파일을 검사한 결과 화면이다.

 

<화면 3> 클래스 분석결과 화면

 

       소스 코드 수정

 

<화면 3>에서는 JCTest.java 파일에서 하나의 Modified Warning이 발생했음을 알 수 있다. <화면 3>의 가운데에 있는 테이블에서 해당하는 Warning을 선택하면, 아래쪽에 있는 Error/Dependency information 박스에 그 Warning에 대한 정보를 보여준다. 그러므로, 이 정보를 참조하여, 소스코드를 PJAE 환경에 맞게 수정을 해 주어야 한다.

 

JCTest.class의 검증 과정에서 발생한 Warning의 내용은 PJAE 1.1에서는 Frame의 생성이 한 번만 가능하고, 다시 Frame을 생성하게 되면 문제가 발생할 것이라는 메시지이다. Warning은 프로그램의 수행에는 영향을 미치지 않으므로, 이 프로그램은 실행이 가능하지만, Warning의 내용에 대해서는 충분한 인지를 할 필요가 있을 것이다.

 

3. 자바 임베디드 서버와 OSGi

 

자바 임베디드 서버(Java Embedded Server, 이하 JES)는 임베디드 디바이스를 위한 퍼스널 자바 기반의 런타임 환경이다. 그리고 JES는 네트워크를 통한 서비스의 제공과 서비스 라이프 사이클의 관리 기능을 제공하는 소용량 애플리케이션 서버라고 할 수 있다. 그렇다면, OSGi와 JES는 어떤 관계일까? OSGi의 표준화를 주도한 업체가 썬 마이크로시스템즈라는 사실을 알게 되면 그 해답은 쉽게 찾을 수 있다. 즉, JES는 OSGi 표준을 구현한 임베디드 서버 솔루션 중의 하나라는 것이다. 현재 JES는 2.0까지 발표되었다.

 

<그림 1>은 JES의 아키텍처를 나타낸 것이다. JES의 개념은 OSGi와 동일하기 때문에 상세한 설명은 뒷부분의 OSGi에 대한 설명으로 대체하도록 하고, 여기에서는 JES의 기본적인 특징만 살펴보도록 하자.

 

<그림 1> JES 아키텍처

 

3.1 JES 2.0의 기본 서비스

 

JES는 프레임워크와 서비스로 구성되어 있다. 서비스는 JES 서버에서 동작하는 콤포넌트화 된 애플리케이션이고, ServiceSpace 프레임워크는 서비스의 컨테이너다. 프레임워크의 기본적인 기능은 서비스들의 설치, 업그레이드, 삭제 등의 라이프사이클에 대한 제어고, 서비스는 서비스 인터페이스에 구현으로 구성된다. 그리고 JAR(Java ARchive) 파일로 패키지화된 서비스를 번들이라고 부른다. 이런 개념들은 뒤에서 설명하게 될 OSGi와 동일하다. 그리고, CLDC에 포함된 JAM(Java Application Manager)의 기능과도 일맥상통하는 부분이 있다.(본지 8월호 참조)

 

JES 서버는 네트워크를 통한 서비스의 설치와 업그레이드, 삭제 등이 가능하지만, 처음에 기동될 때 몇 가지 기본적인 서비스를 포함하고 있다. 다음은 JES 2.0의 코어 서비스들이다.

 

u       HTTP 서비스 - 웹 서버 기능

u       로그 서비스 - 에러와 이벤트 등의 원격 로깅 기능

u       날짜 서비스

u       연결 관리 서비스 네트워크 서비스, 소켓 바인딩, 연결요청 처리 등의 기능

u       쓰레드 매니저 서비스 쓰레드 풀의 최대치 등에 대한 쓰레드 관리 기능

u       스케쥴러 서비스 향후의 이벤트에 대한 스케쥴링 기능

u       RMI(Remote Method Invocation) 서비스

u       SNMP(Simple Network Management Protocol) 서비스

u       콘솔 서비스 애플릿을 통한 원격관리 기능

 

이 중에서 OSGi 1.0 규격에 포함된 표준 서비스는 HTTP 서비스, 로그 서비스, 연결 관리 서비스(OSGi에서는 디바이스 액세스 서비스)다.

 

4. 홈 서버 게이트웨이의 표준 : OSGi

 

이제 OSGi의 기본 개념에 대해서 살펴보도록 하자. OSGi를 구성하는 중요 엔터티는 다음과 같다.

 

u       서비스 : 특정 기능을 수행하는 자바 인터페이스와 실제 구현객체

u       번들 : 서비스를 제공하기 위한 기능적 배포 단위

u       프레임워크(번들 컨텍스트) : 번들의 라이프 사이클을 관리하는 번들 실행환경

 

서비스는 미리 정의된 서비스 인터페이스를 통해 접근이 가능한 컴포넌트다. 하나의 애플리케이션은 여러 개의 서비스의 협동작업을 통해 구성되고, 런타임시에 필요한 서비스를 요청할 수도 있다. 프레임워크는 각 서비스와 그 서비스에 해당하는 실제구현에 대한 매핑을 가지고 있고, 간단한 쿼리 메커니즘을 통해 서비스의 실제구현을 찾을 수 있다. 또한 프레임워크는 각 서비스간의 상호 의존관계를 관리한다.

 

번들은 여러 서비스의 구현을 하나의 패키지로 묶은 JAR 파일의 형태로 존재한다. JAR파일에는 하나 이상의 서비스의 구현객체와 리소스 파일 및 매니페스트 파일을 포함하고 있다. 번들 컨텍스트는 프레임워크 내부의 번들의 실행환경이며, 번들의 설치, 실행, 정지 및 삭제 등의 번들의 라이프 사이클을 관리한다.

 

OSGi 1.0 규격에서 명시한 패키지들은 <표 1>과 같다.

 

<표 1> OSGi 프레임워크 패키지

패키지 명

 

org.osgi.framework

OSGi 자바 서비스 프레임워크

org.osgi.service.device

OSGi 디바이스 액세스 서비스 명세

org.osgi.service.http

OSGi HttpService 명세

org.osgi.service.log

OSGi LogService 명세

 

박스기사 1. 인터넷 정보가전과 홈 네트워킹

인터넷 정보가전을 위한 홈 네트워킹 기술의 표준화는 말 그대로 춘추전국 시대를 맞이하고 있다. 수많은 임베디드 디바이스들 간의 연동과 통합을 가능하게 하는 표준의 제정이  인터넷 정보가전 시장의 선결 조건이므로, 국내외를 막론하고 치열한 표준화 경쟁이 벌어지고 있다. <그림 2>는 이러한 홈 네트워킹 표준들의 기술적 구성을 잘 보여주고 있다.

 

<그림 2> 홈 네트워킹 기술구성도

 

홈 네트워크 기술은 유선 형태인 IEEE1394, HomePNA와 무선 형태인 HomeRF, Bluetooth 등이 있다. 또한 미들웨어로는 HAVi, Jini, UPnP, OSGi가 있다.

 

u       IEEE1394 애플과 텍사스 인스트루먼트가 주도한 A/V 기기간의 데이터 전송을 위한 A/V 기기용 High Performance Serial Bus 기술 표준

u       HomePNA(Home Powerline Network Alliance) 기존의 전화배선을 사용해 고속 댁내망을 구축하는 기술 표준. 가장 저렴한 홈네트워킹 방법.

u       Bluetooth 2.4 GHz 주파수 대역에서 1Mbps 전송속도로 약 10m 거리 이내의 각종 컴퓨터 및 통신 단말기를 무선으로 연결하는 무선 접속 기술의 표준

u       HomeRF(Home Radio Frequency ) 2.4 GHz 주파수 대역에서 1Mbps/2Mbps 전송속도로 약 50m 거리 이내의 각종 컴퓨터 및 가전기기들을 무선으로 연결하는 무선 접속 기술의 표준

u       OSGi(Open Service Gateway initiative) WAN-LAN 장비 상에서 여러 서비스의 제공이 가능한 공개 표준을 제정하여 홈 게이트웨이에 대한 해법을 제공해 주는 서비스 게이트웨이 표준

u       Havi(Home Audio/Video interoperability) - 가정에 있는 네트워크를 통해 연결된 다양한 벤더들의 디지털 오디오와 비디오 장치들 간의 상호 운용성을 제공해주는 소비자 가전 산업의 국제 표준

u       Jini 자바를 기반으로 하여, 네트워크에 접속된 디지털 디바이스 간의 동적인 상호 작용을 가능하게 하는 기술 표준

u       UPnP(Universal Plug and Play) - 기기들 간의 상호접속을 위해서 이루어지는 복잡한 설정작업이나 환경설정 작업을 생략하고 각종 디바이스들이 네트워크에 접속하기만 하면 자동적으로 이 디바이스들을 찾아 사용할 수 있는 기술 표준

 

 

 

 

4.1. org.osgi.framework

 

OSGi 프레임워크는 소용량 메모리 디바이스에서 프로그래밍하는 개발자들이 연속적으로 동작할 수 있는 애플리케이션을 작성할 수 있는 컨텍스트를 제공하는 것이 목적이다. 애플리케이션의 swap-in과 swap-out이 런타임시에 발생하게 되는 이러한 환경에서는 런타임시에 다른 애플리케이션과 구조적이고 의존적인 방식으로 커뮤니케이션을 수행해야 할 필요가 있다. OSGi 프레임워크는 자바 프로그래밍 언어가 가진 코드의 네트웍 이동성을 이용하여 컴포넌트 기반의 개발환경을 제공함으로써, 보다 풍부하고 구조적인 서비스의 개발을 가능하게 한다.

 

OSGi 프레임웍이 제공하고자 하는 환경의 목표는 다음과 같다.

Ø         애플리케이션이 실행 중에도 동적으로 다운로드 및 업그레이드가 가능

Ø         제한된 메모리 디바이스 사용 가능

Ø         효율적이고 통합된 컴포넌트 개발환경 제공

Ø         애플리케이션 간의 의존성에 대한 관리 기능 제공

Ø         확장 가능성(scalable)

 

<그림 3>은 OSG 프레임워크가 관리하는 서비스 번들의 라이프 사이클을 나타내고 있다.

 

<그림 3> 서비스 번들 라이프 사이클

org.osgi.framework 패키지는 10개의 인터페이스와 6개의 클래스, 2개의 예외 클래스로 구성되어 있다. (<표 2>)

 

<표 2> org.osgi.framework 패키지

인터페이스

클래스

예외 클래스

Bundle
BundleActivator
BundleContext
BundleListener
Configurable
FrameworkListener
ServiceFactory
ServiceListener
ServiceReference
ServiceRegistration

AdminPermission
BundleEvent
FrameworkEvent
PackagePermission
ServiceEvent
ServicePermission

BundleException
InvalidSyntaxException

 

4.2. org.osgi.service.device

 

디바이스 매니저는 OSGi 프레임웍의 하나의 서비스 리스너로 등록(register)된다. 디바이스 매니저는 새롭게 추가되는 디바이스의 서비스를 탐지하고, 새로 추가된 디바이스의 드라이버 번들을 설치한다.

 

다음은 디바이스 매니저의 알고리즘이다.

1. 디바이스 탐지(Device Detection)

모든 새로운 Device(org.osgi.service.device.Device 인터페이스를 구현한 객체)를 검색

2. 위치 단계(Location Phase)

새로 추가된 모든 드라이버를 DriverLocator를 이용해서 위치시킴

3. 경선 단계(Bidding phase)

각 드라이버를 디바이스 상에서 경선에 참여시킴

4. 추가 단계(Attach Phase)

경선 단계에서의 최상위 드라이버를 추가함

5. 청소 단계(Cleanup Phase)

idle 드라이버를 청소함

 

각 드라이버 번들은 세계적으로 유일한 스트링 아이디를 가지고 있어야 하고, 이 아이디는 드라이버 매니저가 드라이버의 revision을 해석하고, 프레임웍의 위치 아이디로서 사용된다.

 

org.osgi.service.device 패키지는 3개의 인터페이스로 구성되어 있다. (<표 3>)

 

<표 3> org.osgi.service.device 패키지

인터페이스

클래스

예외 클래스

Device
Driver
DriverLocator

 

 

 

4.3. org.osgi.service.http

 

HttpService는 프레임웍 내의 다른 번들이 리소스를 등록할 수 있게 하고, HTTP를 통해 서블릿에 접근할 수 있게 한다. HttpService를 통해 등록할 수 있는 엔터티는 서블릿과 리소스이다. 서블릿은 Java Servlet API를 구현한 객체이며, 서블릿의 등록은 URI 네임스페이스에 대한 서블릿 제어권을 부여한다. 리소스는 HTML 파일, GIF 파일, 클래스 파일 등을 포함하며, 리소스의 등록은 이러한 리소스들을 URI 네임스페이스에서 사용가능하도록 한다.

 

org.osgi.service.http 패키지는 2개의 인터페이스와 하나의 예외 클래스로 구성되어 있다. (<표 4>)

 

<표 4> org.osgi.service.http 패키지

인터페이스

클래스

예외 클래스

HttpContext
HttpService

 

NamespaceException

 

4.4. org.osgi.service.log

 

LogService는 번들로부터의 로그 요청을 받아들이고, LogReaderService는 다른 번들이 로그 항목을 읽을 수 있도록 한다. LogService의 주 목적은 이벤트와 에러상황에 대한 리포트를 목적으로 하지만, 다른 용도로도 활용할 수 있다.

 

org.osgi.service.log 패키지는 4개의 인터페이스로 구성되어 있다. (<표 5>)

 

<표 5> org.osgi.service.log 패키지

인터페이스

클래스

예외 클래스

LogEntry
LogListener
LogReaderService
LogService

 

 

 

5. OSGi와 쉘위탱고?

 

지금까지 OSGi 1.0 규격에 대해서 살펴보았다. 지금까지의 내용을 보면 임베디드 자바 프로그래밍에 뭔가 독특한 면이 있을 것을 기대한 독자들은 적잖은 실망을 할 수도 있을 것이다. 왜냐하면, PJAE의 개발환경은 조금 원시적인 자바환경(JDK 1.1.8)과 동일하고, OSGi 서비스의 개발은 서블릿의 개발환경과 비슷하기 때문이다. 물론, 그런 장점 때문에 임베디드 시스템에서의 자바 기술이 각광을 받고 있긴 하지만, 퍼스널 자바 프로그래밍과 OSGi 서비스 개발이 기존에 해 오던 일과 별반 다를 것이 없다는 것은 실망스러운 일일 것이다.

 

하지만, OSGi 1.0 규격이 발표된 것이 올해 3월임을 감안할 때, 아직은 걸음마 단계임을 짐작할 수 있다. 그러므로, 반드시 필요하지만, 아직 표준화되지 않은 많은 일들이 남아 있을 것이다. 그래서, 여기에서는 오픈 소스 OSGi인 서비스탱고(ServiceTango) 1.0을 통해 OSGi의 구현과 OSGi 서비스 번들의 구현에 대해서 살펴봄으로써, 향후의 임베디드 서비스 개발의 발전방향에 대한 안목을 기르는 계기로 삼도록 해 보자. 

 

5.1 서비스탱고의 다운로드 및 설치

 

서비스탱고는 테드 스톡웰이 개발한 오픈 소스 OSGi 서버다. 현재 베타 4 단계에 와 있으며, 아직은 디바이스 액세스 프로토콜을 포함한 많은 부분의 구현이 완료되지 않은 상태이지만, 소스와 함께 테스트가 가능하다는 점에서 서비스탱고를 선택했다. 그리고, J2SE 기반으로 개발이 진행되고 있으므로, 퍼스널 자바 환경과는 호환이 되지 않는다.

 

그럼, 우선 서비스 탱고를 설치하고 환경을 설정해 보자.

 

       서비스탱고 다운로드 및 설치

 

다음 사이트에서 서비스탱고를 다운로드하여 압축파일을 푼다.

http://servicetango.sourceforge.net

 

       환경설정

 

서비스탱고의 환경설정은 서비스탱고의 루트 디렉토리에 있는 servicetango.xml 파일에서 지정한다.

 

루트 디렉토리는 설치된 번들을 저장하기 위한 저장공간의 루트를 지정한다. 여기에서 주의해야 할 점은 임베디드 디바이스의 경우 파일 시스템이 없으므로, 영속저장공간에 대한 지원은 서비스탱고의 구현과는 전혀 달라진다는 점이다. 즉, 임베디드 시스템에서는 비휘발성 플래시 메모리나 DOC(Disc On Chip) 등의 영속 저장공간을 가지고 있으므로, 서비스탱고의 스토리지 관련 구현은 대부분 임베디드 시스템에는 그대로 적용할 수 없을 것이다. 하지만, 실질적인 영속저장공간과의 인터페이스는 퍼스널 자바의 java.io 패키지가 담당하므로, OSGi 개발에는 투명할 수 있다.

 

<root-directory>./storage</root-directory>

 

<그림 3>에서 알 수 있듯이 번들의 상태 중에는 STARTING, STOPPING 상태와 같은 일시적인 상태들이 있다. 그러므로, 번들이 이런 상태일 때, 상태 변환 이벤트가 발생하면 일정 기간동안 대기 해야 한다. 이 때의 대기시간을 환경변수로 설정한다.

 

<bundle-timeout-minutes>2</bundle-timeout-minutes>

 

그리고 일반적인 시스템 환경변수, 즉 로그 사이즈, HTTP 포트번호, 사용 프로토콜 등은 일반적인 시스템 변수값으로 지정할 수 있다. 이것은 자바 가상머신의 D 옵션을 통해 환경변수를 지정하는 것과 동일하다.

 

<system-property>

   <name>org.servicetango.service.log.size</name>

   <value>20</value>

</system-property>

<system-property>

   <name>org.servicetango.service.http.port</name>

   <value>8080</value>

</system-property>

<system-property>

   <name>org.servicetango.service.http.protocol</name>

   <value>http</value>

</system-property>

 

       서비스탱고의 기동과 서비스 관리

 

서비스탱고는 다음 배치 커맨드를 통해 실행할 수 있다. 서비스 탱고가 실행되면, 아래와 같이 버전정보와 함께 현재 번들의 상태가 디스플레이 되고, ServiceTango> 라는 프롬프트가 나타난다. 여기에서 help나 ?를 입력해 보면 사용 가능한 명령어들이 디스플레이 된다. 

 

prompt> start_service_tango.bat

ServiceTango v1.0

Copyright (C) 2000 by ted stockwell

All Rights Reserved

 

  Id |      Status | Location

   1 |      ACTIVE | file:bundles\stlog.jar

   3 |      ACTIVE | file:bundles/hello.jar

   2 |      ACTIVE | file:bundles\sthttp.jar

 

ServiceTango>

 

위의 화면에서는 처음 기동될 때 sthttp, stlog, hello 서비스가 ACTIVE 상태임을 알 수 있다. 번들 아이디가 3번인 hello 서비스를 중지하고자 할 때는 다음과 같이 stop 명령어를 입력한다.

 

ServiceTango> stop 3

ContextManager: Removing context Ctx(  )

Ctx(  ): Remove context

Ctx(  ): Pre servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Post servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Context Shutdown

Bundle 3 successfully stopped

 

번들의 정지가 성공적으로 끝나면 bundle 명령어를 통해 다시 현재의 번들 상태를 확인하고, hello 서비스가 RESOLVED 상태인 지 확인한다.

 

ServiceTango> bundle

  Id |      Status | Location

   1 |      ACTIVE | file:bundles\stlog.jar

   3 |    RESOLVED | file:bundles/hello.jar

2 |      ACTIVE | file:bundles\sthttp.jar

 

정지된 서비스를 다시 시작시키기 위해서는 start 명령어를 이용한다.

 

ServiceTango> start 3

Ctx(  ): Add context

ContextManager: Adding context Ctx(  )

Ctx(  ): Context Init

Ctx(  ): Pre servlet init jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Post servlet init jsp(org.apache.jasper.servlet.JspServlet/null)

Bundle 3 successfully started

 

설치된 번들은 uninstall 명령으로 삭제할 수 있다.

 

ServiceTango> uninstall 3

ContextManager: Removing context Ctx(  )

Ctx(  ): Remove context

Ctx(  ): Pre servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Post servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Context Shutdown

Bundle 3 successfully uninstalled

 

새로운 서비스를 설치하기 위해서는 install 명령을 사용한다.

 

ServiceTango> install file:bundles/hello.jar

'file:bundles/hello.jar' successfully installed

 

서비스탱고를 정지시키고자 할 때는 shutdown 명령을 사용한다.

 

ServiceTango> shutdown

ContextManager: Removing context Ctx(  )

Ctx(  ): Remove context

Ctx(  ): Pre servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Post servlet destroy jsp(org.apache.jasper.servlet.JspServlet/null)

Ctx(  ): Context Shutdown

ContextManager: Engine shutdown

 

       클라이언트 접근

 

서비스탱고의 서비스에 접근하는 것은 기존의 웹 서비스에 접근하는 것과 동일하다. 현재의 환경설정이 8080 포트를 이용하므로 웹 브라우저에서 다음 URL로 접속한다.

 

http://localhost:8080/hello

 

5.2 OSGi 프레임워크의 구현

 

OSGi 프레임워크는 서비스 번들의 네트워크를 통한 다운로드, 설치, 업그레이드, 삭제 등의 기능을 제공한다. OSGi 서비스 게이트웨이 명세서는 이러한 프레임워크 기능이 만족해야 하는 명세를 제공하고 있다. 여기서는 지면관계상 프레임워크의 기동과 정지, 번들의 설치, 실행, 정지, 삭제에 대한 부분만 살펴보도록 하겠다.

 

5.2.1 프레임워크 기동과 정지

 

프레임워크의 기동시에는 프레임워크가 정지될 때의 상태가 ACTIVE인 모든 서비스 번들을 기동시켜야 한다. 그 절차는 다음과 같다.

 

       이벤트 핸들링이 가능한 상태로 전환

       설치된 번들의 상태가 ACTIVE인 모든 번들을 실행함. (Bundle.start() 메쏘드 사용)

       STARTED 타입의 FrameworkEvent를 브로드캐스팅함.

 

Framework의 정지 절차는 다음과 같다.

 

       ACTIVE 상태인 모든 서비스 번들을 중지시킨다. 이 때 영속 저장공간의 상태는 계속해서 ACTIVE 상태로 둔다. (재기동시에 실행시키기 위해)

       이벤트 처리를 중지한다.

 

5.2.2 번들의 설치와 삭제

 

번들을 설치할 때는 프레임워크가 BundleContext.installBundle() 메쏘드를 호출한다. 번들의 설치 과정은 다음과 같다.

 

       번들이 이미 인스톨되어 있는 지를 확인한다.

       번들의 코드 베이스로부터 내용을 얻어 온다.

       새로운 번들을 생성하고 관련된 리소스들을 할당한다.

       번들의 상태를 INSTALLED 상태로 변경한다.

       번들의 클래스 패스 및 네이티브 코드 의존성을 해결(resolve)한다.

       INSTALLED 타입의 BundleEvent를 브로드캐스트한다.

       위의 과정에서 예외가 발생하지 않았으면 종료하고, 예외가 발생하면 원래의 상태로 되돌리고 종료한다.

 

서비스 탱고에서는 BundleManager의 installBundle() 메쏘드에서 번들의 설치 과정을 구현하고 있다.

 

번들의 삭제는 Bundle.uninstall() 메쏘드에서 구현되고 삭제 과정은 다음과 같다

 

       번들의 상태가 ACTIVE이면, 이 번들을 정지시킨다.

       UNINSTALLED 타입의 BundleEvent를 브로드캐스팅한다.

       번들의 상태를 UNINSTALLED 상태로 변경한다.

       번들이 점유하고 있는 영속 저장공간을 반환한다.

 

서비스 탱고에서는 BundleManager의 uninstallBundle() 메쏘드에서 번들의 설치 과정을 구현하고 있다.

 

5.2.3 번들의 실행과 정지

 

번들을 실행할 때는 프레임워크가 Bundle.start() 메소드를 호출한다. 번들의 시작 알고리즘은 다음과 같다.

 

       현재의 번들의 상태를 체크한다.

       현재 번들의 상태에 따라 다음과 같이 처리한다.

u       UNINSTALLED IllegalStateException 발생

u       STARTING 혹은 STOPPING - 일정한 시간동안 대기(wait)

u       ACTIVE 막바로 종료함

u       INSTALLED 의존성을 해결(resolve)함. (성공 후 RESOLVED 상태로 전환됨)

u       RESOLVED 다음 단계로 진행

       번들의 상태를 STARTING 상태로 변환함

       BundleActivator.start() 메쏘드 호출

       영속 저장공간에 번들의 상태를 시작된 상태로 기록

       번들의 상태를 ACTIVE로 변환함

       STARTED 타입의 번들 이벤트를 브로드캐스트.

 

서비스 탱고에서는 BundleManager의 startBundle() 메쏘드에서 번들의 설치 과정을 구현하고 있다.

 

번들을 정지시킬 때는 프레임워크가 Bundle.stop() 메소드를 호출한다. 번들의 정지  알고리즘은 다음과 같다.

 

       현재의 번들의 상태를 체크한다.

       현재 번들의 상태에 따라 다음과 같이 처리한다.

u       UNINSTALLED IllegalStateException 발생

u       STARTING 혹은 STOPPING - 일정한 시간동안 대기(wait)

u       INSTALLED 혹은 RESOLVED 막바로 리턴

u       ACTIVE 다음 단계로 진행

       번들의 상태를 STOPPING 상태로 변환함

       영속 저장공간에 번들의 상태를 정지된 상태로 기록

       BundleActivator.stop() 메쏘드 호출

       이벤트 리스너와 이 번들에 의해 등록된 서비스의 등록을 모두 취소한다.

       번들의 상태를 RESOLVED로 변환함

       STOPPED 타입의 BundleEvent를 브로드 캐스트

 

서비스 탱고에서는 BundleManager의 stopBundle() 메쏘드에서 번들의 설치 과정을 구현하고 있다.

 

<그림 4>는 지금까지 살펴본 번들의 설치, 실행, 정지, 삭제 과정에서의 프레임워크와 서비스 번들 간의 상호작용 관계를 잘 보여준다.

 

<그림 4> 프레임워크와 서비스 번들의 상호작용

 

6. OSGi 서비스의 구현

 

그럼 이제 아주 간단한 OSGi 서비스를 구현하는 과정을 살펴 보도록 하자.

 

       서비스 인터페이스 작성

 

가장 먼저 해야 할 일은 작성할 서비스의 인터페이스를 설계하는 일이다. <리스트 1>은 hello()라는 간단한 메쏘드를 가진 서비스의 인터페이스다.

 

<리스트 1> SimpleService.java

package simple.service;

 

/**

 * 간단한 OSGi 서비스 인터페이스

 */

public interface SimpleService {

   public void hello(String name);

}

 

       서비스 인터페이스의 구현객체 작성

 

다음은 설계한 서비스 인터페이스의 실제 구현을 한다. hello() 메쏘드는 일자로 받은 이름을 포함한 간단한 문장을 출력하는 기능을 한다. <리스트 2>은 HelloService 인터페이스를 구현한 클래스다.

 

<리스트 1> SimpleServiceImpl.java

package simple.impl;

 

import simple.service.*;

 

/**

 * 서비스 인터페이스에 대한 구현객체

 */

public class SimpleServiceImpl implements SimpleService {

   public void hello(String name) {

      System.out.println("Hello, " + name);

   }

}

 

       번들의 라이프 사이클 제어를 위한 액티베이터 구현

 

번들 액티베이터는 앞에서 구현한 서비스를 프레임워크에 등록하고 삭제할 때, 리소스의 저장과 반환 등의 기능을 수행하는 인터페이스다. org.osgi.framework.BundleActivator 인터페이스를 구현한 클래스는 start(), stop() 메쏘드의 구현을 제공해야 한다. 두 메쏘드는 각각 서비스의 실행과 정지시에 호출된다. <리스트 3>은 실행시에 단일 이름으로 서비스를 등록하고, 종료시에 등록을 취소하는 번들 액티베이터의 구현이다.

 

<리스트 4> SimpleActivator.java

package simple.impl;

 

import org.osgi.framework.*;

import java.util.*;

import simple.service.*;

 

/**

 * SimpleService 번들 Activator

 */

public class SimpleActivator implements BundleActivator {

 

   private ServiceRegistration svcreg = null;

 

   /**

    * 번들이 시작될 호출되는 메쏘드

    */

   public void start(BundleContext bctx) throws BundleException {

      System.out.println("starting...");

      SimpleService svc = new SimpleServiceImpl();

 

      // 서비스를 하나의 이름으로 등록함

      Properties props = new Properties();

      props.put("description", "simple example");

      svcreg = bctx.registerService(

                  "simple.service.SimpleService", svc, props);

   }

 

   /**

    * 번들이 정지할 호출되는 메쏘드

    */

   public void stop(BundleContext bctx) throws BundleException {

      System.out.println("stopping...");

      if (svcreg != null) svcreg.unregister();

   }

}

 

       번들에 대한 매니페스트 파일 작성

 

서비스 번들을 패키징하기 위해서는 매니페스트 파일에 서비스 속성을 지정해야 한다. SimpleService에서는 번들 액티베이터를 지정하는 Bundle-Activator 속성과 export할 패키지를 지정하는 Export-Package 속성을 지정하도록 한다. 앞에서 서비스의 구현을 simple.impl 패키지와 simple.service 패키지로 구분한 것은 export할 패키지에는 서비스 인터페이스만 존재하는 것이 적당하기 때문이었다.

 

Export-Package: simple.service

Bundle-Activator: simple.impl.SimpleActivator

 

       컴파일 및 JAR 파일 패키징

 

작성이 완료되었으면 명령 프롬프트에서 다음과 같이 컴파일하고, JAR 파일로 패키징한다.

 

prompt> javac -classpath .;%STHome%\lib\framework.jar

simple\impl\*.java

prompt > javac -classpath .;%STHome%\lib\framework.jar

simple\service\*.java

prompt > jar cmf simple.mf simple.jar .

 

       설치 및 실행

 

JAR 파일을 생성했으면, 이 파일을 서비스탱고에 설치하고 실행해 보자. 먼저, simple.jar 파일을 %STHome% 디렉토리 아래의 bundles 디렉토리로 복사하고, 서비스 탱고를 실행한다. 그리고 나서 다음과 같이 인스톨한 후 실행을 해 보자.

 

ServiceTango> install file:bundles\simple.jar

'file:bundles\simple.jar' successfully installed

ServiceTango> bundle

  Id |      Status | Location

   3 |    RESOLVED | file:bundles\simple.jar

   1 |      ACTIVE | file:bundles\stlog.jar

   2 |      ACTIVE | file:bundles\sthttp.jar

ServiceTango> start 3

starting...

Bundle 3 successfully started

 

정상적으로 실행된 상태에서 다음과 같이 services 명령어를 통해 현재 등록된 서비스들에 대한 정보를 볼 수 있다.

 

ServiceTango> services

Service Class:org.osgi.service.log.LogService

Registered By Bundle 1|file:bundles\stlog.jar

This service is not using any other bundles.

Properties:

  objectClass=org.osgi.service.log.LogService

  Description=The standard ServiceTango Log service

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

Service Class:org.osgi.service.log.LogReaderService

Registered By Bundle 1|file:bundles\stlog.jar

This service is not using any other bundles.

Properties:

  objectClass=org.osgi.service.log.LogReaderService

  Description=The standard ServiceTango LogReader service

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

Service Class:org.osgi.service.http.HttpService

Registered By Bundle 2|file:bundles\sthttp.jar

This service is not using any other bundles.

Properties:

  objectClass=org.osgi.service.http.HttpService

  Description=The OSGI HTTP Service

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

Service Class:simple.service.SimpleService

Registered By Bundle 3|file:bundles\simple.jar

This service is not using any other bundles.

Properties:

  objectClass=simple.service.SimpleService

  description=simple example

 

7. 임베디드 시스템과 네트워크 외부성

 

메트카프의 법칙에 따르면, 네트워크 외부성의 크기는 네트워크 참가자의 제곱에 비례한다고 한다. 예를 들면, 전화 가입자가 각각 2명인 네트워크가 서로 결합하면 양쪽 모두가 통화 기회가 증대되므로, 네트워크의 크기가 4가 아니라 16이 된다는 것이다. 다양한 임베디드 디바이스들 간의 네트워킹에 있어서도, 어느 한 쪽의 표준이 전체 시장을 장악하지 않는 이상, 서로 다른 네트워크를 통합하는 기술들이 무엇보다도 중요하게 될 것이다.

 

이번 특집에서는 OSGi라는 화두를 중심으로 임베디드 디바이스를 위한 자바 기반의 동적인 서비스 모델의 응용에 대해서 살펴보았다. OSGi가 그 자체로서 의미를 가지는 것은 아니다. 하지만, 지니, UPnP, HAVi와 같은 다른 미들웨어 솔루션과의 연결고리를 할 수 있다는 측면에서 OSGi의 중요성은 간과할 수 없을 것 같다. 지니와 UPnP, HAVi 기술에 대해서는 본지의 2000년 2월호 특집을 참고하면 될 것이다.

 

처음 원고를 시작할 때는 서비스탱고가 구현하지 않은 디바이스 액세스 서비스와 보안 관련 클래스, 의존성 해결(dependency resolution) 부분을 구현해 보고, 서비스탱고의 퍼스널 자바 포팅 과정을 다루고자 하는 욕심이었지만, 개인적인 사정으로 인해 단순한  소개 차원에서 그칠 수밖에 없었음이 아쉽다. 죄송스러운 일이지만, 이 부분은 의욕이 넘치는 독자들의 몫으로 돌리도록 하겠다.

 

 

필자 연락처 : 배준현(promy72@netsgo.com)

 

[참고자료]

1. J2ME 홈페이지, http://java.sun.com/j2me

2. PersonalJava 홈페이지, http://java.sun.com/products/personaljava

3. Java Embedded Server 홈페이지, http://java.sun.com/products/jes

4. ServiceTango 홈페이지, http://www.servicetango.org

5. Java Embedded Servier Tutorial, Sun Microsystems

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

출처 : http://www.mobilejava.co.kr/

배준현님의 마소 연재 강의

 

댓글 없음:

댓글 쓰기