월별 글 목록: 2008년 5월월

전시회 도우미 – 각종 전시회 연간 일정표 한눈에 보기 – 아웃룩/구글캘린더/다음캘린더/iCal(MacOS) 지원

전시회 가는거 좋아하시나요 ?

전 COEX 에 영화보러 갈때 보통 같이 볼수 있는 전시회들도 체크해 뒀다가 꼭꼭 들르는 편입니다. 요즘 전시회 들은 사전에 등록만 하면 공짜로 볼수 있는 전시회가 많죠.

그런데 이런 전시회 일정을 체크하려면 각 전시회 사이트에 가서 플래시에 온갖 복잡한 UI들을 뚫고 전시회 정보를 찾아 봐야 합니다. 그래서 매년 전시회 일정을 편하게 볼수 있는 페이지를 하나 만들었습니다. 이름하야..

구루의 전시회 도우미

예전에 만든 “구루의 지름 도우미”가 인기가 좋아서, 시리즈로 이름 붙여봤습니다. 🙂

전시회 도우미에 들어가면 아래와 같이 좀 밋밋한 전시회 일정 정보를 볼수 있습니다.

구루의 전시회 도우미

현재 COEX , KINTEX, SETEC 세군데의 전시일정 정보를 보여주고 있습니다. 매년/매달 자동으로 읽어다가 업데이트 합니다. 참고로..

  • COEX는 다들 아시는 서울 삼성동 무역센터에 있는 것으로, 예전엔 한국종합전시장 지금은 그냥 주식회사 코엑스 입니다.
  • KINTEX는 경기도 고양시 일산에 있는 한국국제전시장 입니다.
  • SETEC은 서울 강남구 대치동(학여울역)에 있는 서울무역전시컨벤션 센터입니다.

기본으로는 오늘부터 약 한달 뒤까지의 전시 일정만 보여줍니다. 원하시면 상단의 [2008년 전체보기]를 눌러서 일년 전체 일정을 보실수 있습니다. 물론 2008년은 내년에 자동으로 2009년으로 변경됩니다. 전시회 테이블에서 전시회 제목을 보시고 관심있으신 전시회 사이트로 가서 자세한 정보를 읽고나서 사전등록 하시면 됩니다. 각 전시회 사이트 옆의 [상세정보] 는 각 전시장에서 제공하는 간략화된 상세 정보 페이지로 가는 링크입니다.

원하는 전시회에 사전등록하고 나면, 이 전시회의 일정을 사용중인 일정관리 프로그램에 추가하시면 됩니다.
사용하시는 일정관리 프로그램에 따라 추가하는 방법은 아래쪽에 정리후 부분별로 접어두었습니다. 사용하시는 툴별로 확인하세요.

현재 지원하는 각종 환경은 다음과 같습니다.

  1. Windows : Internet Explorer/Firefox 브라우저 + Outlook/Sunbird [ 테스트 완료 ]
  2. MacOS : Safari + iCal [ 테스트 완료 ]
    이건 테스트는 했는데 스크린샷은 없습니다. 제 주위에 맥 유저가 없어서 영화보러 코엑스 나간김에 애플샵에서 동작 확인했습니다. ㅡ.ㅡ;
    Safari 에서 전시회도우미 페이지 열고 Add iCalendar 버튼 누르니, iCal 에서 추가 잘 되더군요. 스크린샷이 가능하신 맥 유저께서 아래처럼 스크린샷 보내주시면 제가 올려놓겠습니다. ^^;
  3. 인터넷서비스 : 구글 캘린더 ( http://calendar.google.com ) , 다음 캘린더 ( http://calendar.daum.net ) [ 테스트 완료 ]
  4. Mobile : iPod Touch, iPod [ 브라우징 가능 , 일정추가 불가능 ]
    제 블로그의 iPhone/iPod Touch 전용 페이지에서 확인은 되지만, 아쉽게도 iPod Touch 에 들어있는 사파리는 iCalendar MIME 타입이 연결이 안되어서 Calendar로 추가하려고 할때 에러가 납니다. 인터넷에 알아보니 iPhone/iTouch 는 iCalendar 포맷을 지원하지 않는다고 합니다. 아마도 이번에 나올 3G 버전에는 추가되지 않을까 생각이 되네요. 맥 유저께선 iCal 에 넣고 싱크해서 사용하시거나, WIFI 이용해서 실시간 확인용으로만 쓰시기 바랍니다. 🙂

    제 블로그의 iPhone 전용페이지 주소는 https://xguru.net/iphone/ 입니다. (iPhone에선 https://xguru.net 만 입력하셔도 이리로 연결됩니다.) IE 에서는 화면이 제대로 안보입니다. Firefox 에서는 아래처럼 얼추 비슷하게 보실수 있습니다. iPhone 전용 페이지 소개는 나중에 한번 자세히 소개글을 써봐야 겠네요. ^_^

    블로그 iPhone 버전전시회 도우미 iPhone 버전

  5. Mobile : 일반 핸드폰 ( SKT,KTF,LGT ) [브라우징 가능, 일정추가 불가능]
    제 블로그는 원래 핸드폰으로도 접속이 가능합니다. 핸드폰 무선인터넷 모드에서 https://xguru.net 을 입력하시면 각 폰에 맞는 페이지로 분기되며, 현재 7가지의 모바일용 페이지를 지원합니다. WML, WML 2.0, XHTML, UPWML, MHTML, KUN (KTF), PDA (스마트폰 계열)

    제 핸드폰인 SKT 에서만 해봤는데 아래처럼 간단히 보입니다. 다른 폰에서도 문제는 없을듯 합니다. 이건 데이터통신비용이 발생하므로 데이터정액요금제 쓰시는 핸드폰 사용자 분들만 접속해서 확인 해보세요. 핸드폰 페이지에서는 핸드폰 특성에 맞게 바로 전시회 측으로 전화걸기도 가능합니다. ^^;

  6. Mobile : Clie , PSP [ 브라우징 가능, 일정추가 불가능 ]
    제가 사용하는 Clie TH-55 의 경우는 해상도가 320X480 이라 특별히 다른 모드를 지원하고 있습니다. https://xguru.net/m/ 에서 접속 가능하구요. 클리에 사용자께서는 NetFront 브라우저에서 https://xguru.net 만 입력해도 알아서 이쪽 페이지로 분기됩니다.

    클리에 동작화면

여기서 말씀드리지 않은 색다른 환경에서 테스트해보신 분은 저한테 스크린샷 보내주시면 추가하겠습니다 ^^;

아래는 각 사용법 스크린샷입니다. 길어서 접어두었으니, 필요하신 부분만 펼쳐서 보시면 됩니다.

[#!_전시회 별로 구글 캘린더에 추가 방법 펼치기.. (ADD GOOGLE 버튼 )▽|구글 캘린더 추가완료~ 다시 접어두기..△_!#]

구글 캘린더에 추가하기

오른쪽 끝의 일정등록 항목의 2개의 아이콘 중 구글 캘린더에 추가는 구글 캘린더에 해당 전시회 일정을 추가합니다. 구글 캘린더에 추가하는것은 사용하시는 분의 현재 환경에 따라 두가지 경우가 있을수 있습니다.

  1. 기존에 다른 구글 서비스 ( 메일, 캘린더, 개인화 홈페이지 등)에 로그인 되어있는 상태 – Google Event Publisher API 사용
  2. 다른 구글 서비스에 로그인이 되어 있지 않은 상태 – 페이지 위측의 이곳에서 구글 로그인 링크 사용(Google GData Auth API 사용)

두개의 방법이 별로 차이는 없지만, 2번의 경우는 제 블로그가 여러분의 구글 캘린더 정보에 접근할 수 있도록 권한을 부여해야 하므로, 정보공개가 싫으신분들은 되도록 1번 방법을 이용해 주세요. 물론 전 여러분의 어떤 일정정보도 읽지 않을것입니다. 저 나쁜놈 아닙니다 믿어주세요! ^^/ 2번 방법이 구현이 더 복잡하기도 하고, 괜히 사용자분들께 의심살만한 요소를 남기는거 같아서 2번 방법을 넣고 싶지 않았는데, 1번의 경우 만약 여러분께서 다른 구글 서비스에 로그인한 상태가 아니라면 클릭시 에러 페이지가 떠버려서 어쩔수 없이 2번도 추가해 놓았습니다.

1번의 경우 구글 서비스에 로그인 되어있을 경우 클릭하면 다음과 같이 구글 캘린더의 등록 화면이 보여집니다.

구글 캘린더에 추가

위와 같은 화면이 뜨고, 자신의 캘린더에 등록하면 됩니다. 등록된 화면은 다음과 같습니다.

전시회 추가됨

2번의 경우는 이곳에서 구글 로그인 링크를 누를경우 구글로 이동하여 아래와 같은 구글 로그인 창이 뜹니다.

구글 Account 로그인

여기서 여러분의 구글 계정정보를 입력후 로그인 하면, 아래처럼 여러분의 구글 캘린더 계정에 지름도우미 페이지가 접속하는것을 허용하겠냐는 물음이 뜹니다. 다시 한번 말씀드리지만 싫으시면 그냥 취소 눌려주셔도 됩니다. ^^;

접속 허용

확인 눌러주시면 다음과 같이 이벤트가 추가 되었다는 간단한 정보 창이 뜹니다. 여기서 다시 전시회도우미로 돌아가거나 구글 캘린더 페이지로 가셔서 추가하신 일정을 확인하실수 있습니다.

일정 추가 완료

[#!_END_!#]

[#!_전시회 별로 Outlook,다음캘린더,iCal,Sunbird, Lightning등에 추가 방법 펼치기..(ADD ICALENDAR 버튼)▽|아웃룩/iCal 추가완료 ~ 다시 접어두기..△_!#]

iCalendar 파일로 Outlook,다음캘린더,iCal,Sunbird, Lightning등에 추가하기

iCalendar 파일로 추가는 표준 포맷인 iCalendar포맷으로 전시회 일정을 받습니다. 클릭하면 윈도우 사용자는 Outlook 이나 Sunbird 혹은 Lightning 같은 iCalendar 포맷에 연결되어있는 일정 관리 프로그램으로 열기 라는 메뉴가 보일것입니다. 전 주로 아웃룩을 쓰므로 아래와 같습니다.

Outlook 으로 열기

그냥 확인을 누르면 파일을 받아서 , Outlook 에서 다음과 같은 창이 뜹니다.

Outlook 에 추가하기

아웃룩은 상세정보에 태그같은것을 지원하지 않아서 조금 보기 뭐하지만 그럭저럭 볼만합니다. 🙂

[#!_END_!#]

[#!_아웃룩/iCal에 일년치 전시회 일정 모두 추가하는 방법 펼치기..▽|아웃룩/iCal에 일년치 일정 추가완료~ 다시 접어두기..△_!#]

아웃룩/iCal 에 일년치 전시회 일정 모두 추가하기

1년치 전시회 일정을 다 받아서 보시려면 맨 위의 [iCalendar 파일로 2008년 전체 다운로드(UTF-8)]을 이용하세요. 그냥 클릭하면 아래처럼 Outlook 에 전체 임포트 됩니다.

전체 일정
[#!_END_!#]

[#!_다음 캘린더에 일년치 전시회 일정 모두 추가하는 방법 펼치기..▽|다음 캘린더에 일년치 일정 추가완료~ 다시 접어두기..△_!#]

다음 캘린더에 일년치 전시회 일정 추가하기

Daum 캘린더에서도 위와같이 [iCalendar 파일로 2008년 전체 다운로드(UTF-8)]를 받아서 환경설정->가져오기 에서 파일을 지정하시면 됩니다. 내용이 많으므로 꼭 새로운 카테고리를 만들어서 이용하세요
Daum 캘린더에서도 위의 구글 캘린더처럼 바로 일정 추가 가능한 링크를 만들어주면 편할듯 한데.. 찾아봐도 없더군요.

Daum 전체일정
[#!_END_!#]

[#!_구글 캘린더에 일년치 전시회 일정 모두 추가하는 방법 펼치기..▽|구글 캘린더에 일년치 일정 추가완료~ 다시 접어두기..△_!#]

구글 캘린더에 일년치 전시회 일정 추가하기

구글 캘린더에서도 전체 일정 임포트가 가능은 한데, 문제가 하나 있는게 구글 캘린더의 iCal 임포트는 UTF-8 로 된 문서를 읽을때 오류를 냅니다. 구글 캘린더에 전체 임포트시에는 [EUC-KR버전]를 받아서 설정->캘린더->캘린더추가->캘린더 가져오기에서 파일을 지정하시면 됩니다.

Daum 전체일정
[#!_END_!#]

2008.05.27 : RSS로도 제공됩니다. http://feeds.feedburner.com/coex를 구독해주세요. 한RSS로 구독하기

Getting Started with OSGi 한글판 번역완료 및 PDF 배포

EclipseZone 에 연재되었던 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역이 완료되었습니다. 제 블로그가 워낙에 이쪽으로는 관련없던지라.. 오시는 분들 및 구독하시는 분들의 호응은 별로 없어서 슬펐지만.. ㅠ.ㅠ

혹시 OSGi 를 시작하는 분들이 검색중에 보시고 도움이 되었으면 합니다.

전체 링크와 각 파트별 PDF 파일을 공개합니다. 전 이런 강좌들 뽑아서 보는게 좋더군요. ^_^

이제 Neil 의 책 “OSGi in Practice” 번역작업에만 몰두할 예정입니다. 이건 아마 Neil 이 원본을 배포하는 방식에 따라, 챕터당 번역이 되는대로 PDF 파일로만 공개할듯 합니다.

* 위 PDF 파일은 전에 말씀드렸던 Office 2007 용 PDF 변환 애드온 – SaveAsPDF로 작성되었습니다.

OSGi 시작하기 8 – 선언적 서비스와 의존관계 : [번역] Declarative Services and Dependencies

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework
* 파트 3 – 번들간의 의존관계 : Dependencies between bundles
* 파트 4 – 서비스 등록하기 : Registering a Service
* 파트 5 – 서비스 사용하기 : Consuming a Service
* 파트 6 – 동적으로 서비스 추적하기 : Dynamic Service Tracking
* 파트 7 – 선언적 서비스 소개하기 : Introducing Declarative Services
에 이어 8번째 마지막 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 8 – 선언적 서비스와 의존관계

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t97690.html – Getting started with OSGi : Declarative Services and Dependencies

“Getting Started with OSGi” 시리즈에 돌아 오신걸 환영합니다. 레슨 시작전에 한가지 공지, 이 튜토리얼의 이전회들을 보시려면 제 블로그 페이지에서 링크들을 찾아보실수 있습니다.

지난번에는 선언적 서비스에 대해 처음으로 살펴봤습니다. 이번에는 선언적 서비스의 사용자 측면을 살펴볼 것입니다. 지난번에 java.lang.Runnable 인터페이스로 서비스를 등록했다는것을 기억하세요. 이제 우리는 그 서비스에 의존하는 컴포넌트를 만들어 볼 것입니다.

말씀드린대로, 선언전 서비스에 대한 스펙은 이전 레슨들에서 보았던 OSGi 의 글루(glue)코드보다 여러분 코드의 Application Logic 에 집중할수 있도록 해주는 것입니다. 이걸 염두에 두고, 코드를 살펴보겠습니다. 그전에 우린 프로젝트를 하나 만들어야 합니다. 이전 레슨의 순서를 따라서, 이번엔 “SampleImporter” 라는 이름으로 프로젝트를 생성하여 보세요.

새로 생성된 Eclipse 프로젝트에 아래 코드를 복사하여 src 폴더에 붙여넣습니다.

package org.example.ds;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
public class SampleCommandProvider1 implements CommandProvider {
  private Runnable runnable;
  public synchronized void setRunnable(Runnable r) {
   runnable = r;
 }
 public synchronized void unsetRunnable(Runnable r) {
   runnable = null;
 }
 public synchronized void _run(CommandInterpreter ci) {
   if(runnable != null) {
      runnable.run();
   } else {
     ci.println("Error, no Runnable available");
    }
 }
 public String getHelp() {
    return "\trun - execute a Runnable service";
 }
}

이 클래스는 Equinox 를 실행할때 osgi> 프롬프트 상에서 사용가능한 명령들을 확장하는 CommandProvider 인터페이스를 구현합니다. CommandProvider 를 작성하는 이유는, 그게 우리 코드를 인터랙티브하게 테스트하기 편하기 때문입니다. 커맨드 프로바이더에 대해서는 IBM developerWorks 에 Chris Aniszczyk 의 기사에 더 자세한 내용이 있습니다.

우리가 이 클래스에서 어떤 OSGi API 도 호출하지 않는다는 것을 주목하세요. 실제로 우리는 org.osgi.* 패키지에서 아무것도 import 할 필요도 없습니다. 우리가 의존하는 서비스는 java.lang.Runnable 의 인스턴스이며 setRunnable 메소드로 우리에게 전달되며, unsetRunnable 메소드에 의해 치워집니다. 우리는 이것을 dependency injection 의 형태로 볼수 있습니다.

다른 두개의 메소드 getHelp _run 은 커맨드 프로바이더에 대한 구현 메소드들 입니다. 네, “_run” 은 앞에 밑줄이 있는 웃기는 이름입니다만, 이것은 Equinox console APIs 의 이상한 기능이며 OSGi 나 선언적 서비스와는 상관이 없습니다. 메소드의 이름 앞에 밑줄을 넣는 패턴은 Equinox console 에서 명령이 되므로, _run 메소드를 제공한다는 것은 우리가 “run” 명령을 추가했다는것입니다. 이 클래스에서 주의할 다른점은, runnable 필드가 업데이트/접근이 쓰레딩에 안전한 형태로 이루어져야 한다는 것에 주의해야 합니다. OSGi 가 본질적으로 멀티쓰레드이기 때문에 쓰레드 안전은 중요합니다. 우린 항상 우리의 코드를 쓰레딩에 안전하게 작성해야 합니다.

전과 같이, 우린 이것이 가능하게 하는 DS 선언을 가진 XML 파일을 제공해야 합니다. 아래 코드를 OSGI-INF/commandprovider1.xml 에 복사하세요.

<?xml version="1.0"?>
<component name="commandprovider1">
  <implementation class="org.example.ds.SampleCommandProvider1"/>
  <service>
   <provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
  </service>
  <reference name="RUNNABLE"
    interface="java.lang.Runnable"
   bind="setRunnable"
   unbind="unsetRunnable"
   cardinality="0..1"
   policy="dynamic"/>
</component>

제가 지난번에 말하지 않은 중요한 단계가 있습니다. ( 지적해준 Seamus Venasse 에게 감사드립니다.) 여러분의 플러그인 프로젝트안에 build.properties 를 수정하여, OSGI-INF 폴더옆 체크박스를 켜야합니다. 이것은 번들이 Eclipse export wizard 를 이용하거나 PDE 빌드를 이용해서 Export 될때 해당 폴더가 꼭 포함되도록 하기위해 필요합니다. 또한 아래 라인을 번들 Manifest 에 추가해야 합니다.

Service-Component: OSGI-INF/commandprovider1.xml

위 DS 선언은 이전에 우리가 봤던것과 같은 implementationservice 두개의 같은 항목을 가지고 있습니다. implementation 노드는 컴포넌트를 실제 구현하는 클래스의 이름을 표시하며, service 노드는 DS 에게 컴포넌트를 서비스로 등록하도록 알려줍니다. 이 경우는 우리가 CommandProvider 인터페이스를 이용하여 등록함으로써, Equinox 콘솔이 우리 커맨드 프로바이더 의 존재를 알게 됩니다.

다음 엘리먼트인 reference 는 우리가 이전에 보지 못한것으로, DS 에 우리가 서비스에 의존하고 있다는 것을 선언해 줍니다. name 속성은 의존관계의 이름을 나타내는 문자열이며(이게 어디에 쓰이는지는 아직 알 필요가 없습니다) interface 속성은 우리가 의존하는 인터페이스 이름을 나타냅니다. bind 속성은 서비스가 사용 가능할때 DS 에 의해 호출될 구현클래스의 메소드 이름이며, 다시 말해서 Runnable 서비스가 서비스 레지스트리에 등록될때 DS 가 새로운 서비스 객체에 대한 레퍼런스를 얻어서 이 지정 메소드를 통해 우리에게 전달해 준다는 것입니다. 비슷하게 unbind 속성은 서비스가 사용 불가능해 질때 DS 에 의해 호출될 메소드 명입니다.

cardinality 속성은 DS 의 진정한 힘을 보여줍니다. 이 속성은 의존성이 필수(mandatory)인지 선택(optional)인지, 단일인지 다수인지를 지정하게 해줍니다. 가능한 값들은
다음과 같습니다.

  • 0..1: 선택이면서 단일관계 , “0 또는 1” – “zero or one”
  • 1..1: 필수이면서 단일관계 , “딱 1개” – “exactly one”
  • 0..n: 선택이면서 복수관계 , “0 에서 여러개” – “zero to many”
  • 1..n: 필수이면서 복수관계 , “1 에서 여러개” 또는 “적어도 한개 이상” – “one to many” or “at least one”

이 예제에서는 우린 옵션이면서 단일관계를 선택했으므로, 이것은 우리의 커맨드 서비스가 의존하는 서비스가 없더라도 대처할수 있다는것을 의미합니다. _run 메소드를 다시 살펴보면 이런 상황을 대처하기 위해 서비스 핸들에 대체 null 체크가 필요했다는것을 보실수 있습니다.

이 번들을 실행했을때 어떤 일이 일어나는지 봅시다. 지난번에 작성했던 SampleExporter 번들을 아직 실행중에 있다면, osgi> 프롬프트에서 “run” 명령을 입력했을때 아래 출력을 볼 수 있습니다.

    Hello from SampleRunnable

멋집니다. 이것은 우리가 지난번 레슨에서 작성한 Runnable 서비스가 성공적으로 import 되었다는것을 알려줍니다. 이제 stop 명령을 이용해서 SampleExporter 번들을 중단시킵니다. 그리고 다시 “run” 명령을 입력하면 아래와 같은 메시지를 볼수 있습니다.

    Error, no Runnable available

이것으로 보아, DS 가 Runnable 서비스가 없어져 버려서 우리의 unsetRunnable 메소드를 호출했다는것을 알수 있습니다.

다시 cardinality 속성을 살펴보면, 만약 우리가 “1..1” 로 변경해서 선택에서 필수 의존관계로 바꾸면 어떤일이 생길까요 ? 한번 변경하고 Equinox 를 재실행 해보세요. 만약 SampleExporter가 활성화 되어있다면 우리가 “run” 을 입력했을때 이전과 같은 “Hello from SampleRunnable” 메시지를 볼수 있습니다. 하지만 만약 SampleExporter가비활성화 되어 있다면 전혀 다른 메시지를 볼수 있습니다. 실제로 우린 모르는 명령을 입력했을때 나오는 Equinox 콘솔의 도움말을 보게 됩니다. 이것은 우리의 Command Provider 자체가 DS 에 의해 등록해제 되었다는 것을 의미합니다! 컴포넌트가 필수 의존관계를 충족시키지 못하기 때문에 DS 가 컴포넌트를 비 활성화 하고 제공하던 서비스 자체를 등록해제 한것입니다. 그래서 Equinox 콘솔이 run 명령 자체를 모르게 된것이죠.

이런 쉬운 변경이 우리가 선언적 서비스를 이용하는 가장 큰 이유중 하나입니다. 우리가 ServiceTracker 를 이용할때는 꽤 많은 양의 코드를 재작성해야 했다는것을 기억하세요.

여러분께선 제가 아직 언급하지 않은 policy 속성이 궁금하실 것입니다. 이 속성의 값은 static 또는 dynamic 이 될수있으며, dynamic은 서비스가 동적으로 변경될때 컴포넌트의 implementation이 이에 동적으로 대응이 가능한지를 말해줍니다. static이라면 DS가 해당 서비스가 변경될때마다 컴포넌트를 비활성화하고 새로운 인스턴스를 만들어야 합니다. 이건 매우 부하가 많은 접근방법이며, 여러분께선 가능하면 동적 변경을 지원하도록 컴포넌트 클래스를 코딩하는것이 좋습니다. 아쉽게도 static 이 기본값이기 때문에 dynamic 을 명시적으로 지정해주어야 합니다.

그럼 지금까지 선택 단일 및 필수 단일 관계를 살펴보았습니다. 그럼 복수관계는 어떨까요 ? 서비스 레지스트리에는 한개이상의 Runnable 이 등록되어있을구 있구, 그중 한가지와 연결(bind)할때 어떤것을 선택하는지는 임의로 결정됩니다. 아마도 우린 현재 등록된 모든 Runnable 을 실행하도록 하는 “runall” 명령을 개발할 수도 있습니다.

만약 우리가 cardinality 를 “0..n” 으로 바꾸면 무슨일이 벌어질까요 ? 아마 거의 동작할 겁니다. 다만 setRunnalbe 을 한번 호출하는것 대신, DS 는 레지스트리의 Runnable 인스턴스들의 setRunnable을 한번씩 호출할 것입니다. 문제는 우리가 만든 클래스가 혼란스러울수 있다는 것입니다. 한개의 Runnable 필드를 세팅하는것보다 Runnable 들을 컬렉션에 넣어두는것이 필요합니다. 아래에 약간 수정된 버전의 클래스가 있습니다. 아래 코드를 복사해서 프로젝트의 src 폴더에 붙여넣기 하실수 있습니다.

package org.example.ds;
import java.util.*;
import org.eclipse.osgi.framework.console.CommandInterpreter;
import org.eclipse.osgi.framework.console.CommandProvider;
public class SampleCommandProvider2 implements CommandProvider {
 private List runnables =
   Collections.synchronizedList(new ArrayList());
  public void addRunnable(Runnable r) {
   runnables.add(r);
 }
 public void removeRunnable(Runnable r) {
    runnables.remove(r);
  }
 public void _runall(CommandInterpreter ci) {
    synchronized(runnables) {
      for(Runnable r : runnables) {
        r.run();
      }
   }
 }
 public String getHelp() {
    return "\trunall - Run all registered Runnables";
  }
}

자 이제 OSGI-INF/commandprovider2.xml 를 만들고 아래를 복사합니다.

<?xml version="1.0"?>
<component name="commandprovider2">
 <implementation class="org.example.ds.SampleCommandProvider2"/>
  <service>
   <provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
  </service>
  <reference name="RUNNABLE"
    interface="java.lang.Runnable"
   bind="addRunnable"
   unbind="removeRunnable"
    cardinality="0..n"
   policy="dynamic"/>
</component>

그리고 마지막으로 이 파일을 manifest 의 Service-Component 헤더에 추가합니다. 이렇게 보일것입니다.

Service-Component: OSGI-INF/commandprovider1.xml,
  OSGI-INF/commandprovider2.xml

이 선언은 우리가 bind,unbind 메소드의 이름을 변경하고, cardinality를 “0..n”으로 변경했다는것을 제외하면, 이전과 거의 비슷합니다. 실험삼아 몇개의 Runnable 서비스들을 등록하고 runall 명령이 동작하는지를 살펴보세요. 다음으로 만약 우리가 cardinality 를 “1..n”으로 바꾸면 어떻게 될까요 ? 어떻게 될지를 한번 생각해보고 확인해 보세요.

레슨은 여기까지 입니다. OSGi Alliance Community Event 가 다음주에 독일 Munich 에서 열린다는것을 잊지마세요. 아직 등록하기에 늦지 않았습니다. 거기서 봅시다! (역주: 역시 지나버린 이벤트입니다만.. 그냥 번역해 둡니다.)

OSGi 시작하기 7 – 선언적 서비스 소개하기 : [번역] Introducing Declarative Services

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.

* 파트 1 – 첫번째 번들 : Your First Bundle

* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework

* 파트 3 – 번들간의 의존관계 : Dependencies between bundles

* 파트 4 – 서비스 등록하기 : Registering a Service

* 파트 5 – 서비스 사용하기 : Consuming a Service

* 파트 6 – 동적으로 서비스 추적하기 : Dynamic Service Tracking

에 이어 7번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 7 – 선언적 서비스 소개하기

저자 : Neil Bartlett < njbartlett at gmail dot com >

역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t96740.html – Getting started with OSGi : Introducing Declarative Services

매우 늦어져 버린 “Getting Started with OSGi” 시리즈에 오신걸 환영합니다. 이번 회는 선언적 서비스에 대해 알아볼것이므로 매우 재미있을 것입니다.

선언적 서비스(Declarative Services , DS) 스펙은 OSGi 의 새로운 부분중의 하나로서, 번들간의 서비스를 연결할때 생기는 이슈들의 결과로 생겨났습니다. 이 작업이 어렵다는 것이 아니라 ( 제 예전 튜토리얼들이 보여주었을꺼라고 희망합니다. ) 꽤 많은 양의 반복적인 코드(boilerplate)를 필요로 하기 때문입니다. 또한, 쓰레드 문제때문에 조심해야 하며 이는 쉽게 당신의 발에 총을 쏠수 있게 된다는 것입니다. (shoot yourself in the foot, 역주: 쉽게 실수할 수 있다는 것으로, 그냥 원문대로 번역했습니다. http://burks.bton.ac.uk/burks/language/shoot.htm 참고하세요)

이 문제를 해결하기 위한 초기 시도는 Humberto CervantesRichard Hall 이 만든 툴인 Service Binder 였습니다. Service Binder 는 automated service dependency management(자동 서비스 의존관계 관리) 를 소개하여 개발자에게 자신들의 서비스만을 작성하는데 집중하도록 했습니다. 서비스간의 연결은 선언적으로 처리되었고, 이 선언들은 XML 로 작성되었습니다.

선언적 서비스의 스펙은 Service Binder 에서 발전된것으로 이제는 OSGi 버전 4.0 이상에선 표준이 되었습니다. 그럼, 이제 이것이 우리에게 무엇을 해줄수 있는지 보죠.

지난회에서 말씀드렸듯이, 이제 커맨드 라인에서 모든것을 하는 것이 힘들어졌으므로 지금부터는 Eclipse SDK 를 사용하도록 하겠습니다. 그렇지만 제가 소개하는 것들이 정말 Eclipse 에 종속적이지는 않다는것을 기억해주세요. 비록 Eclipse 가 우리가 하려는 것들을 많이 도와주기는 하지만, 어떤 흑마법도 없으며, 여기서 보시는 어떤 것도 NetBeans 나 IntelliJ 또는 오래된 vi 라도 가능합니다.

첫번째로 할일은 선언적 서비스에 대한 Equinox 구현을 다운로드 하는것입니다. 안정적인 Eclipse 버전을 사용하고 있으시다는 가정하에 ( 이글 쓰는 시점에서는 3.2.2 ), 다운받기 링크는 여기 입니다. 다른 버전을 사용하고 계시다면, 여기 Equinox 의 상위레벨 다운로드 페이지 에서 맞는 버전의 org.eclipse.equinox.ds_x.x.x_xxxxx.jar 파일을 찾으셔야 합니다. 다운로드가 끝나면, Eclipse가 설치된 디렉토리 하부의 plugins 디렉토리에 복사하고 Eclipse 를 재시작 하세요. 만약에 여러분이 Eclipse plug-ing 개발에 약간 알고 계신다면, 이 Jar 파일을 Target Platform 폴더에 넣으셔야 합니다. “Target Platform”이 뭔지 모르신다면, 아무 걱정마시고 그냥 plugins 폴더에 JAR 파일을 넣어주세요.

자 이제 새로운 번들을 만들 것입니다. Eclipse 에서 Plug-in 프로젝트 위자드를 이용해서 새로운 프로젝트를 생성합니다.

  1. 메인 메뉴에서 File -> New -> Project 를 선택합니다.
  2. Plug-in 프로젝트를 선택하고 Next를 누릅니다.
  3. 프로젝트의 이름을 SampleExporter 라고 입력합니다.
  4. 아래쪽 “This plug-in is targeted to run with” 항목 밑에, “an OSGi framework” 를 선택하고 그 옆 드롭다운리스트에서 “standard” 를 선택하세요. 이 단계는 꼭 필요한>것은 아니지만, 우리가 실수로라도 다른 OSGi 프레임워크 구현들이 기본으로 가지고 있은 기능(Out-of-the-box)외의 것들을 사용하지 못하도록 막아줍니다.
  5. Next 를 누릅니다. 다음에 나오는 Wizard 페이지에서, “Generate an activator…” 의 체크박스를 해제하고, Finish 를 누릅니다.

이제 우린 빈 번들 프로젝트를 하나 가지게 되었으므로, 코드를 추가해 봅니다. 일을 쉽게 하기위해 , 모든 자바 런타임에 들어있는 인터페이스인 java.lang.Runnable 을 이용하는 서비스를 만들어 봅니다. 이클립스 단축키를 이용해서, 아래코드를 브라우저에서 직접 복사한후에 SampleExporter 프로젝트에 있는 src 폴더를 선택하고, Edit->Paste 를 선택해보세요. Eclipse 가 패키지와 소스파일을 만들어 줄것입니다.

package org.example.ds;
public class SampleRunnable implements Runnable {
 public void run() {
   System.out.println("Hello from SampleRunnable");
 }
}

지금까지는 특별한게 없습니다만, 이제 재미있어지는 부분이 나옵니다. 우린 SampleRunnable 을 서비스로 선언하는 XML 파일을 생성할 것입니다. 프로젝트 최상단에 OSGI-INF 라는 폴더를 만들고 그 안에 samplerunnable.xml 파일에 아래 내용을 복사하세요.

<?xml version="1.0"?>
<component name="samplerunnable">
 <implementation class="org.example.ds.SampleRunnable"/>
  <service>
   <provide interface="java.lang.Runnable"/>
  </service>
</component>

이것은 우리가 DS 에서 보게될 간단한 선언 형식중 하나입니다. 내용인 즉슨, java.lang.Runnable 의 인터페이스를 이용하여 OSGI Service Registry에게 서비스를 제공하는 samplerunnable 이라는 컴포넌트가 있고, 컴포넌트의 구현 클래스는 org.example.ds.SampleRunnable 이라는 것입니다. 마지막 단계는 Declaratie Services 런타임에게 XML 파일의 존재를 알리는 것입니다. 우린 이것을 번들의 MANIFEST.MF 에 필드를 추가함으로써 가능합니다. Manifest를 열고(프로젝트에서 우측버튼 클릭후 PDE Tools -> Open Manifest를 이용해보세요), 직접 manifest 편집이 가능한 MANIFEST.MF 탭으로 가서 아래 라인을 추가합니다.

Service-Component: OSGI-INF/samplerunnable.xml

그리고는 파일을 저장합니다. 이전회에서처럼, manifest 의 맨 마지막 빈줄을 매우 중요합니다. 하지만 이전과는 다르게 만약 당신이 잊는다해도 Eclipse 가 에러메시지를 출력할 것입니다.

더 진행하기 전에, Equinox 를 실행해서 이것이 동작하는지 봅시다. Run 메뉴로 가서 “Run…” 메뉴를 선택해서 다이얼로그가 열리면 “Equinox OSGi Framework” 를 왼쪽 트리에서 선택하고 New 버튼을 누릅니다. 만약 여러분이 Eclipse 에는 경험이 있지만, OSGi 개발은 안해보셨다면, 이 런쳐 다이얼로그는 약간 이상해 보일것입니다. Bundles 라고 이름붙여진 첫번째 탭은 Launch 설정에서 어떤 번들이 선택될 지, 초기화시에 자동으로 시작할지,아니지를 결정할수 있게 해줍니다. 최소의 세트를 가지려면, Deselect All 을 누르고, 아래 번들들만을 선택합니다.

  • SampleExporter (Workspace 항목 하부에)
  • org.eclipse.equinox.ds (Target Platform 항목 하부에)

이 번들들이 우리가 관심을 가지고 있는것이지만, 이들은 의존관계를 가지고 있으므로, Add Required Bundles 를 눌러서 다른 번들들을 선택되게 합니다. “Validate bundles automatically prior to launching”(실행시 자동으로 번들 검증) 을 선택하는것도 좋은 생각입니다. 마지막으로 Run 을 선택하면 바로 osgi> 프롬프트가 Eclipse 콘솔에 표시될것입니다. OSGi 프레임워크가 실행중이므로, 이전에 배웠던 커맨드들을 사용해 볼수 있습니다.

우리 서비스가 등록되었나요 ? services 명령으로 확인해 볼수 있습니다. 서비스 리스트중에-아마도 마지막쯤에-아래와 같은것을 보실수 있습니다.

{java.lang.Runnable}={component.name=samplerunnable, component.id=1, service.id=22}
  Registered by bundle: initial@reference:file:..../SampleExporter/ [5]
  No bundles using service.

네 등록되었습니다! 주목할건 서비스 등록이 Declarative Services 번들에 의해서가 아니라 우리 번들에 의해 이루어 졌다는 것입니다. 실제로 DS 가 우리 번들을 대신하여 등록해준것입니다. 뒤에 어떻게 그렇게 하는지를 살펴볼 것이지만, 이 레슨의 목적은 우리 서비스의 사용자가 어떤 특별한 일을 할 필요가 없다는것입니다. 실제론 사용자는 우리가 Declarative Services 를 사용하고 있다는것을 알 필요도 없습니다. 사용자 또한 원한다면 Declarative Services 를 사용할 수 있지만, 또한 그냥 OSGi 코드를 직접 이용할 수도 있습니다.

또 하나 주목할것은, 우리의 번들에는 어떤 OSGi 관련 자바 코드도 없다는 것입니다. 다른말로, 우리가 생성한 클래스는 POJO 이고, 이것이 Declarative Services 의 주요 기능입니다.

이번회는 여기까지입니다. 이번 레슨이 너무 기초적인것이라 미안합니다. 다음회에서는 DS 를 이용하여 어떻게 서비스를 사용하는지 알아봄으로써 DS가 제공하는 파워와 편리함을 알수 있을것입니다.

OSGi 시작하기 6 – 동적으로 서비스 추적하기 : [번역] Dynamic Service Tracking

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework
* 파트 3 – 번들간의 의존관계 : Dependencies between bundles
* 파트 4 – 서비스 등록하기 : Registering a Service
* 파트 5 – 서비스 사용하기 : Consuming a Service
에 이어 6번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 6 – 동적으로 서비스 추적하기

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t91059.html – Getting started with OSGi : Dynamic Service Tracking

EclipseZone OSGi 미니 시리즈에 돌아오신걸 환영합니다.

지난번에는 Martin Fowler 의 시나리오(특정감독에 의해 제작된 영화를 찾는 MovieFinder 에 의존하는 MovieLister)를 이용해서 어떻게 서비스를 사용하는지를 알아봤습니다. 또한, MovieLister MovieFinder 의 인스턴스를 찾지 못할때 무엇을 해야하는지를 통해 동적인 OSGi 서비스의 본질에 대해 살펴보았습니다.

우리가 지난번에 고려하지 않았던 다른 것이 있습니다. 만약 한개 이상의 MovieFinder가 있다면? 즉, 어떤 번들이라도 MovieFinder 인터페이스를 구현하는 서비스를 등록할수 있고, 레지스트리 입장에선 모든 번들은 같습니다.

우린 문제를 간단히 무시할 수도 있고, 실제로 지난번엔 그렇게 했습니다. ServiceTracker 에게 getService()를 호출함으로써, 우린 서비스 레지스트리에 의해 선택된 임의의 MovieFinder 인스턴스를 받게 됩니다. 이 선택에 영향을 줄수있는 여러가지 방법이 있지만(예를 들어, SERVICE_RANKING 속성을 서비스 등록시에 지정함으로써), 사용자로서의 우리는 이 선택에 대한 완벽한 조정능력을 가지지는 못합니다. 그리고 실제로 더 많은 조정능력을 가지게 되는것은 좋은게 아닙니다. 즉 우리는 MovieFinder 의 어떤 인스턴스라도 사용할수 있어야 한다는것이죠. 우리가 처음부터 인터페이스를 사용하는 이유이기도 합니다.

대신에 여러개의 서비스 인스턴스들에 대해 인지하고 사용하는 것은 때때로 유용할수도 있습니다. 예를 들어, 여러개의 MovieFinder 인터페이스가 가용하다면 MovieLister 가 이용할수 있는 영화 데이타에 대한 여러개의 소스가 있다는것을 의미합니다. 검색시에 이들을 모두 이용함으로써, 더 넓고 유용한 검색결과를 사용자에게 제공할 수 있게됩니다.

마지막에 논의했던 문제로 다시 돌아가서, 만약 MovieFinder 서비스가 사용 불가하다면 어떤일을 하는것이 맞을까요 ? 우린 MovieFinder 가 가용하지 않을때 listByDirector 메소드가 호출될때마다 null 을 리턴하는 간단한 방법을 사용했습니다. 하지만 MovieFinder 가 존재하지 않는다면 MovieLister 의 메소드 호출도 볼가능하게 하면 어떨까요 ?

MovieListerMovieFinder 와 마찬가지로 서비스입니다. 만약 MovieFinder 서비스가 사라진다면, MovieLister 도 같이 사라지게 하는건 ? 다시 말해, 우린 MovieLister 서비스가 MovieFinder 에 대해 “1 대 다” 관계를 가지기를 원했습니다. 마지막 튜토리얼에서 우린 “0 대 1” 관계였습니다.

MovieListerImpl 를 변경해봅시다. 아래 코드와 교체하세요.

package osgitut.movies.impl;
import java.util.*;
import osgitut.movies.*;
public class MovieListerImpl implements MovieLister {
  private Collection finders =
   Collections.synchronizedCollection(new ArrayList());
  protected void bindFinder(MovieFinder finder) {
   finders.add(finder);
    System.out.println("MovieLister: added a finder");
 }
  protected void unbindFinder(MovieFinder finder) {
   finders.remove(finder);
   System.out.println("MovieLister: removed a finder");
 }
  public List listByDirector(String director) {
    MovieFinder[] finderArray = (MovieFinder[])
     finders.toArray(new MovieFinder[finders.size()]);
    List result = new LinkedList();
    for(int j=0; j < finderArray.length; j++) {
      Movie[] all = finderArray[j].findAll();
      for(int i=0; i < all.length; i++) {
        if(director.equals(all[i].getDirector())) {
          result.add(all[i]);
       }
     }
   }
   return result;
 }
}

이제 우린 MovieListerImpl 에서 OSGi 에 대한 의존성을 없앴습니다. 이젠 순수 POJO 입니다. 어쨋든, 이 클래스는 누군가 또는 어떤것이 MovieFinder 서비스를 찾아서 bindFinder 메소드를 통해 제공되어야 합니다. 이렇게 하기위해서 osgitut/movies/impl/MovieFinderTracker.java 라는 새로운 파일을 아래와 같이 작성합니다.

package osgitut.movies.impl;
import org.osgi.framework.*;
import org.osgi.util.tracker.*;
import osgitut.movies.*;
public class MovieFinderTracker extends ServiceTracker {
  private final MovieListerImpl lister = new MovieListerImpl();
  private int finderCount = 0;
  private ServiceRegistration registration = null;
  public MovieFinderTracker(BundleContext context) {
   super(context, MovieFinder.class.getName(), null);
  }
  private boolean registering = false;
  public Object addingService(ServiceReference reference) {
   MovieFinder finder = (MovieFinder) context.getService(reference);
    lister.bindFinder(finder);
    synchronized(this) {
     finderCount ++;
      if (registering)
        return finder;
      registering = (finderCount == 1);
      if (!registering)
       return finder;
    }
   ServiceRegistration reg = context.registerService(
   MovieLister.class.getName(), lister, null);
    synchronized(this) {
     registering = false;
      registration = reg;
    }
   return finder;
  }
 public void removedService(ServiceReference reference, Object service) {
   MovieFinder finder = (MovieFinder) service;
    lister.unbindFinder(finder);
   context.ungetService(reference);
    ServiceRegistration needsUnregistration = null;
   synchronized(this) {
     finderCount --;
      if (finderCount == 0) {
       needsUnregistration = registration;
        registration = null;
      }
    }
   if(needsUnregistration != null) {
      needsUnregistration.unregister();
    }
  }
}

이 클래스는 지난번에 얘기했던 ServiceTracker 클래스를 오버라이드 해서, ServiceTracker 가 서비스 등록/삭제시 동작하는 방식을 커스터마이즈 합니다. 특징적으로, MovieFinder 서비스가 추가될때 addingService 메소드가 호출되고 , MovieFinder 가 제거될때 removedService 가 호출됩니다. 우리가 오버라이드 할수 있는 메소드중에 modifiedService 도 잇습니다만 여기선 필요하지 않습니다.

이 두개의 메소드에 대해선 살펴볼만한 가치가 있습니다. 첫째로 addingService 에선 전달받은 인자가 실제 서비스 구현 개체가 아니라 ServiceReference 입니다. ServiceReference는 인자로 전달될수 있는 가벼운 핸들이며, 서비스에 대한 속성(서비스 등록시에 전달했던)를 읽는데 사용이 가능합니다. 결정적으로, ServiceReference 를 얻는것은 OSGi 프레임워크가 해당 서비스의 레퍼런스 카운트를 증가시키지 않습니다. Java Reflection API 에 있는 WeakReference 클래스와 비슷하게 생각하시면 됩니다.

예제에서 우리가 ServiceReference 로 할 첫번째 작업은 MovieFinder 서비스 개체를 얻는것입니다. 이를 위해서 우린 다시 BundleContext 를 필요로 합니다. (OSGi 프레임워크와 관련된 모든 작업은 BundleContext 인터페이스를 통해 이뤄진다는걸 명심하세요. ) 편리하게도 슈퍼클래스인 ServiceTracker BundleContext 에 대한 레퍼런스를 context 라고 이름붙인 protected 멤버로 가지고 있으므로 바로 사용할 수 있습니다.

다음으로 우리가 위에서 정의한 MovieListImpl bindFinder 메소드를 가지고 파인더를 바인딩 합니다. 그리고 MovieListerImpl 자체를 MovieLister 인터페이스로 서비스에 등록합니다. 이미 서비스가 등록되지 않았을때만 서비스를 등록하고 있다는 걸 주의하세요. 이번 시나리오 에선 한개의 MovieLister 가 여러개의 MovieFinder 서비스를 추적하기를 원합니다.

마지막으로 메소드에서 리턴합니다. 여기에 흥미있는 부분이 있습니다. addingService 의 리턴타입은 그냥 Object 입니다. 우린 무엇을 리턴해야 할까요 ? 사실은 ServiceTracker 는 상관하지 않습니다. 우리가 원하는 어떤것을 리턴해도 됩니다. 어쨋거나, 우리가 addingService 에서 리턴하는 객체는 modifiedService removedService 가 호출되었을때 우리에게 다시 주어집니다. 그러므로 removeService 메소드의 첫번째 줄을 보시면, 우리가 객체를 MovieFinder 로 캐스팅하는것을 볼수있습니다. 이렇게 해서 리스터로 부터 unbound 될수 있는것이죠. 그리고는 만약 추적하는 finder 들이 0개가 되었을때 MovieLister 서비스를 등록해제 합니다.

일반적으로, 우리가 addingService 에서 한 일들은 removedService 메소드에서 되돌려 놔야 합니다. 그러므로, addingService 메소드에서 리턴할것은 우리가 무슨일을 했는지를 알수있도록 도와주는 것이야 합니다. HashMap 에서의 Key가 될 수도 있고, ServiceRegistration 객체일수도, 이번 경우처럼 실제 서비스 객체일수도 있습니다.

removedService 의 마지막 단계로, 우리가 “가져왔던(Got)” 서비스를 “돌려줘야(Unget)” 합니다. 이는 서비스 레지스트리가 서비스 사용 카운터를 줄이게 해서, 카운터가 0 이 되었을때 서비스가 해제될수 있도록 하기위한 것이므로, 매우 중요합니다.

자 이제 우린 Activator 가 필요합니다. osgitut/movies/impl/TrackingMovieListerActivator.java 입니다.

package osgitut.movies.impl;
import org.osgi.framework.*;
public class TrackingMovieListerActivator implements BundleActivator {
 private MovieFinderTracker tracker;
 public void start(BundleContext context) {
    tracker = new MovieFinderTracker(context);
   tracker.open();
 }
  public void stop(BundleContext context) {
   tracker.close();
  }
}

Manifest 파일 TrackingMovieLister.mf 입니다.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tracking Movie Lister
Bundle-SymbolicName: TrackingMovieLister
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.TrackingMovieListerActivator
Import-Package: org.osgi.framework,
 org.osgi.util.tracker,
 osgitut.movies;version="[1.0.0,2.0.0)"

이 번들을 빌드해서 Equinox 에 설치하는것은 여러분께 연습으로 남겨둡니다. 그후에는 아래 단계들을 따라해서 모든것이 동작하는지를 살펴보세요.

  1. BasicMovieFinder 를 실행하고 TrackingMovieLister 를 실행합니다. “MovieLister: added a finder” 메시지가 나오는지 확인.
  2. services 명령을 입력해서 MovieLister 가 등록되어 있는지 확인합니다.
  3. BasicMovieFinder 를 중단하고 “MovieLister: removed a finder” 메시지가 나오는지 확인.
  4. services 명령을 다시 입력해서 MovieLister 서비스 등록이 해제 되었는지 확인.

우리가 여기서 한 작업은, 매우 강력한 기술의 토대를 다진거라고 볼수 있습니다. 우리는 한개의 서비스의 라이프사이클을 다른 서비스(실제로는 여러개의 다른 서비스)의 라이프사이클에 연결하였습니다. 이 기술을 더 발전시키면, MovieLister 에 연결된 다른 서비스를 가질수도 있고, 또 거기에 의존하는 다른 서비스 등등.. 우리는 서로 의존하는 서비스들의 그래프를 만들었습니다. 하지만 다른 정적인 IOC 컨테이너에 의해 만들어진 bean 들의 그래프와는 달리 우리의 그래프는 튼튼하며, 자가 치유능력이 있고, 변화하는 환경에 적응할 수 있습니다.

한편으로 우리가 작성한 코드에는 약간의 문제가 있습니다. MovieFinderTracker TrackingMovieListerActivator 클래스는 반복사용 되는 문장들이 가득해서, 우리가 시스템을 확장하기 시작한다면 우리는 같은 코드를 매번 조금씩만 바꿔서 작성하게 되어 매우 피곤해 질것입니다. 그러므로 다음회에서는 이 모든 코드를 몇줄의 XML 로 변경하는 방법을 알아볼 것 입니다.

다음회부터는 이제 한 디렉토리에서 커맨드 라인 툴로 모든것을 작성하는것을 그만둘 것입니다. 이 튜토리얼을 시작할때 저의 목표는 OSGi 는 간단하면서도 강력한 프레임워크 이며, OSGi 번들을 개발하기 위해서 Eclipse 같은 무겁고 강력한 IDE 가 필요하지는 않다는것을 보여주는 것 이었습니다. 어떤 것이 매우 쉽다고 보여질때 , 우리는 한편으론 IDE 가 어떤 흑마법을 사용한 것이 아닐까 하는 의구심을 가지게 됩니다. 전 이번 경우 엔 그런것과 달리, OSGi 가 그런 흑마법은 필요하지 않다는 것 을 보여주었기를 희망합니다. 한편으로, 만약 이 코드를 넣는 디렉토리가 제것과 비슷하다면, 여러분께서도 이제 적절한 개발환경을 원하고 계실겁니다. 저 역시 그렇습니다. 이것은 물론 OSGi 의 문제가 아닙니다. 어떤 Java 프로젝트라도 표준 툴만을 이용한다면 금방 관리하기 힘들어 집니다.

하여튼, 죄송하지만 다음회까지 여러분께선 EclipseCon 이 끝나는것을 기다리셔야 할듯합니다. 전 24시간안에 Santa Clara 로 가는 비행기를 탑니다. 거기서 뵙기를!
(역주: 작년글이라 위 내용은 필요없겠지만 원문과의 일치를 위해 그냥 번역합니다 ^_^ )

Concurrent Code 를 위해 더 나은 방법을 제공해준 BJ Hargrave 에게 고맙다는 말을 전합니다.

OSGi 시작하기 5 – 서비스 사용하기 : [번역] Consuming a Service

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework
* 파트 3 – 번들간의 의존관계 : Dependencies between bundles
* 파트 4 – 서비스 등록하기 : Registering a Service
에 이어 5번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 5 – 서비스 사용하기

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t90796.html – Getting started with OSGi : Consuming a Service

마지막 글에서 우린 어떻게 서비스를 등록하는지에 대해 알아봤습니다. 이제 그 서비스를 찾고 다른 번들에서 그 서비스를 이용하도록 작업해 봅시다.

Martin Folwer의 Dependency Injection에 대한 글에서 영감을 얻었던 지난번처럼, 우린 문제를 우리 요구사항에 포함할 것입니니다. 우린 MovieFinder 를 서비스로 만들어서 서비스 레지스트리에 등록했습니다. 이젠 특정 감독에 의해 제작된 영화를 찾기위해 MovieFinder 를 사용하는 MovieLister를 만들어볼 것입니다. 그리고 이 MovieLister 자체도 GUI Application 같은 다른 특정번들에 사용되는 서비스가 될것이라고 가정합니다. 여기서 문제점은, OSGi 서비스는 동적이라 등록되고 사라지고 한다는것입니다. 즉, 우리가 MovieFinder 서비스를 호출하고자 할때, 서비스가 불가능할수도 있습니다.

그러면, 만약 MovieFinder 서비스가 존재하지 않는다면 MovieLister 는 무엇을 해야할까요 ? MovieLister 가 하는 일중에 MovieFinder 에 대한 호출이 매우 중요한 것이라는것은 확실하므로, 우리가 선택할수 있는 옵션은 몇가지 밖에 없습니다.

  1. 에러를 낸다. (Null 을 리턴하거나 Exception 을 내거나)
  2. 기다린다.
  3. 처음부터 아예 설치가 안되도록 한다. ( Don’t be there in the first place )

이 글에선 쉬운 앞에 2가지 방법만을 살펴보도록 합니다. 세번째 방법은 아직 여러분께 어떤 의미가 있게 보이진 않겠지만, 앞에 2가지를 보고나면 알수 있게 되실겁니다.

첫번째로 할일은 MovieLister 서비스에 대한 인터페이스를 작성하는것입니다. 아래 소스를 osgitut/movies/MovieLister.java 에 복사하세요.

package osgitut.movies;
import java.util.List;
public interface MovieLister {
    List listByDirector(String name);
}

이제 osgitut/movies/impl/MovieListerImpl.java 파일을 생성합니다.

package osgitut.movies.impl;
import java.util.*;
import osgitut.movies.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
public class MovieListerImpl implements MovieLister {
    private final ServiceTracker finderTrack;
    public MovieListerImpl(ServiceTracker finderTrack) {
        this.finderTrack = finderTrack;
    }
    public List listByDirector(String name) {
        MovieFinder finder = (MovieFinder) finderTrack.getService();
        if(finder == null) {
            return null;
        } else {
            return doSearch(name, finder);
        }
    }
    private List doSearch(String name, MovieFinder finder) {
        Movie[] movies = finder.findAll();
        List result = new LinkedList();
        for (int i = 0; i < movies.length; i++) {
            if(movies[i].getDirector().indexOf(name) > -1) {
                result.add(movies[i]);
            }
        }
        return result;
    }
}

이것이 아마 지금까지 샘플중 가장 긴 코드일겁니다! 그럼, 여기선 무슨일이 일어나고 있을까요 ? 첫번째로 실제 영화를 찾는 로직이 doSearch(String,MovieFinder) 메소드로 분리되어, OSGi 에 관련된 코드를 독립시키도록 도와주고 있습니다. 불가피 하게도, 우리가 검색을 하는 방법이 멍청하고 비효율적이지만, 그건 이 튜토리얼에는 크게 종요하지 않습니다. 어차피 우리 데이터베이스에는 딱! 2개의 영화만 있으니까요.

흥미로운건 서비스 레지스트리로부터 MovieFinder 를 찾기위해 ServiceTracker를 사용하는 listByDirector(String name) 메소드 입니다. ServiceTracker 는 OSGi API 의 재미없는 하위레벨 코드를 추상화시켜주는 매우 유용한 클래스입니다. 어쨋거나, 우린 정말 서비스가 존재하는지 체크할 필요가 있습니다. ServiceTracker 는 생성자로 전달되었다고 가정합니다.

여러분께선 아마도 ServiceTracker 를 사용하지 않고 레지스트리에서 서비스를 가져오는 코드를 보셨을수도 있습니다. 예를 들어 BundleContextgetServiceReferecegetService 를 호출하는것이 가능합니다. 하지만 이런 코드는 꽤 복잡하고 사용후 잘 지우도록 조심해야 합니다. 제 의견으론, 로우레벨 API를 사용하는 것의 이점은 매우 작고 많은 문제가 있습니다. ServiceTracker 를 항상 사용하는것이 좋습니다.

ServiceTracker 를 생성하기에 좋은곳은 bundle activator 입니다. 아래 코드를 osgitut/movies/impl/MovieListerActivator.java 에 복사하세요.

package osgitut.movies.impl;
import java.util.*;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import osgitut.movies.*;
public class MovieListerActivator implements BundleActivator {
    private ServiceTracker finderTracker;
    private ServiceRegistration listerReg;
    public void start(BundleContext context) throws Exception {
        // Create and open the MovieFinder ServiceTracker
        finderTracker = new ServiceTracker(context, MovieFinder.class.getName(), null);
        finderTracker.open();
        // Create the MovieLister and register as a service
        MovieLister lister = new MovieListerImpl(finderTracker);
        listerReg = context.registerService(MovieLister.class.getName(), lister, null);
        // Execute the sample search
        doSampleSearch(lister);
    }
    public void stop(BundleContext context) throws Exception {
        // Unregister the MovieLister service
        listerReg.unregister();
        // Close the MovieFinder ServiceTracker
        finderTracker.close();
    }
    private void doSampleSearch(MovieLister lister) {
        List movies = lister.listByDirector("Miyazaki");
        if(movies == null) {
            System.err.println("Could not retrieve movie list");
        } else {
            for (Iterator it = movies.iterator(); it.hasNext();) {
                Movie movie = (Movie) it.next();
                System.out.println("Title: " + movie.getTitle());
            }
        }
    }
}

이제 Activator 가 재미있어 보이기 시작합니다. 처음에 start 메소드에서 MovieLister 가 사용할 ServiceTracker 개체를 생성합니다. 그리고는 ServiceTracker 를 “엽니다”. 이건 MovieFinder 서비스의 Instance를 레지스트리에서 추적하는걸 시작하라는 것이죠. 그리고는 MovieLister 인터페이스 이름으로 서비스 레지스트리에 MovieListerImpl 개체를 생성하고 등록합니다. 마지막으로, 우리가 번들을 시작했을때 뭔가 재미있는것이 보일수 있도록, MovieLister 를 이용하여 간단한 검색을 실행하고 결과를 출력합니다.

이제 이 번들을 만들고 설치해야 합니다. 자세한 설명은 하지 않도록 하겠습니다. 이전 회를 참고하면 충분히 하실수 있을겁니다. Manifest 파일을 만들고, Bundle-Activatorosgitut.movies.impl.MovieListerActivator 를 지정해야 하는걸 기억해 두세요. 또한 Import-Package 에는 우리가 다른 번들에서 임포팅하는 3개의 패키지 ( org.osgi.framework , org.osgi.util.tracker 그리고 osgitut.movies ) 를 적어주어야 합니다.

MovieLister.jar 를 Equinox 런타임안에 설치했다면, 시작할 수 있습니다. 지난번에 만든 BasicMovieFinder 번들이 동작하고 있는지에 따라 다음 2개의 메시지중 하나를 볼수 있습니다. 만약 동작중이 아니라면,

osgi> start 2
Could not retrieve movie list

아직 동작중이라면

osgi> start 2
Title: Spirited Away

각 번들을 중지시키고 시작시켜보면, 두개의 메시지를 원하는 대로 보실수 있습니다. 이것이 이번 연재의 거의 다입니다만, 제가 서비스가 사용 불가능할때(not available) 할수 있는 일중에 기다리는 방법이 있다고 한거 기억하시나요 ? 우리가 가지고 있는 코드만 있으면 이건 간단합니다. MovieListerImpl 의 16번째 라인을 getService() 메소드 대신 ServiceTrackerwaitForService(5000)으로 변경하시고, InterruptedException 에 대한 try/catch 블록을 추가하세요.

이것은 listByDirector() 메소드가 5000 밀리세컨드동안 MovieFinder 서비스가 나타날때까지 기다리도록 할것입니다. 만약 MovieFinder 서비스가 그 시간내에 설치된다면 (물론 이미 있을때에도) 우린 바로 서비스를 가져와서 사용이 가능할 것입니다.

일반적인 경우에 저는 이런식으로 중지하고 있는 쓰레드를 사용하라고 권고하지만, 이 경우엔 listByDirecor() 메소드가 프레임워크 쓰레드로 부터 호출되는 Bundle activator 의 start 메소드 에서 호출이 되었기 때문에 위험할수 있습니다. 번들이 활성화 될때 여러 일들이 일어나야할 필요가 있기때문에 Activator 들은 빨리 리턴해야 합니다. 최악의 경우에는 우리가 다른것에 의해 이미 lock 되었을지도 모르는 프레임워크 소유개체의 synchronized 블록에 들어갔으므로 deadlock 이 발생할 수도 있습니다. 기본 가이드라인은, 프레임워크에 의해 호출되는 모든코드 또는 번들의 activator 에 있는 start 메소드에서는 시간이 오래 걸리는 작업이나 블록킹 오퍼레이션을 하지 말라는 것입니다.

다음 회에선 의존개체가 없을 경우의 신비한 3번째 옵션 “Don’t exist in the first place” 에 대해 알아보겠습니다. 기대해 주세요!

OSGi 시작하기 4 – 서비스 등록하기 : [번역] Registering a Service

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework
* 파트 3 – 번들간의 의존관계 : Dependencies between bundles
에 이어 4번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 4 – 서비스 등록하기

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t90688.html – Getting started with OSGi : Registering a Service

EclipseZone OSGi 미니시리즈에 돌아오신걸 환영합니다. 드디어 서비스에 대해 알아볼 준비가 되었습니다. 제 생각에 서비스 레이어는 OSGi 의 가장 재미있는 부분입니다. 그러니 다음 몇회는 아주 재미 있을겁니다.

지난시간에 우린 MovieLister 에 의해 영화를 찾기 위해 사용 되는 MovieFinder 인터페이스 예제에 대해 살펴보았습니다. 실은 아마 여러분도 이 예제를 알고 계셨을겁니다. 이건 Martin Fowler의 유명한 글 인 “Dependency Injection” (또는 “Inversion of Control” / IoC 라고 알려진 )에 나오는 예제입니다.

IoC 가 풀려고 했던 문제를 다시 살펴봅시다. MovieLister 는 어디서 Movie 에 대한 정보들이 오던지 상관하지 않으므로, 우리는 MovieFinder 인터페이스를 이용하여 그런 세부사항들을 숨겼습니다. MovieLister 가 특정 구현이 아닌 인터페이스에만 의존하고 있기 때문에, 우린 MovieFinder 구현을 데이타베이스 에서 읽어오거나 심지어는 Amazon Web Service 를 호출하는것으로 교체할수 있다는 것입니다.

지금까지는 좋습니다. 하지만 특정부분에서는 우리는 MovieLister 에게 MovieFinder 의 구현을 넘겨주어야만 합니다. 이를 위해 MovieLister 가 lookup하는 메소드를 호출하기 보다는 외부 컨테이너를 하나 두어서 적절한 개체를 MovieLister 에게 넣어주는(push) 방법으로 처리합니다. 이것이 우리가 “Inversion of Control” 이라고 부르는 것이죠. PicoContainer, HiveMind, Spring 그리고 EJB 3.0 처럼 수많은 컨테이너들이 개발되었습니다. 그러나 이런 컨테이너들에겐 아직도 하나의 제한이 있습니다. 거의 static 이라는 것이죠. 한번 MovieLister 에게 MovieFinder 가 주어진다면, JVM 의 생명주기동안 연결된채로 남아있는 경향이 있습니다.

하지만 OSGi 는 우리에게 동적인 방식으로 IoC 패턴을 구현하도록 해줍니다. MovieLister 에게 동적으로 MovieFinder 의 구현을 제공할수 있고, 후에 제거할수도 있습니다. 따라서 우린 평범한 텍스트파일에서 영화를 찾는 어플리케이션에서 Amazon Web Service 를 통해 영화를 찾는 Application 으로 실행중 교체(Hot-swap)가 가능합니다.

Service 계층이 우리가 이런 일을 할수있도록 도와줍니다. 간단히 말해 MovieFinder 를 Service Registry 에 서비스로 등록합니다. 후에 MovieFinder 서비스는 MovieLister 에게 제공될수 있습니다. 서비스는 그냥 자바 개체(POJO : Plain Old Java Object)와 특별히 다를게 없으며 Java Interface 이름으로 등록됩니다. ( POJI 라고 해야되나요 ? )

이번 회에서는 레지스트리에 서비스를 등록해 볼것입니다. 차후에 레지스트리에서 서비스를 가져오고 어떻게 이것이 어떻게 MovieLister 에게 제공되는지를 살펴보겠습니다.

지난번에 만들었던 BasicMovieFinder 번들을 추가할것입니다. 기존 클래스를 수정할 필요는 없고, 단지 번들 Activator 만 추가하면 됩니다. 아래 내용을 osgitut/movies/impl/BasicMovieFinderActivator.java 파일에 복사하세요.

package osgitut.movies.impl;
import org.osgi.framework.*;
import osgitut.movies.*;
import java.util.Properties;
import java.util.Dictionary;
public class BasicMovieFinderActivator implements BundleActivator {
    private ServiceRegistration registration;
    public void start(BundleContext context) {
        MovieFinder finder = new BasicMovieFinderImpl();
        Dictionary props = new Properties();
        props.put("category", "misc");
        registration = context.registerService(
                               MovieFinder.class.getName(),
                               finder, props);
    }
    public void stop(BundleContext context) {
        registration.unregister();
    }
}

자 이제 BasicMovieFinder.mf 파일의 내용을 아래로 교체하세요.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Basic Movie Finder
Bundle-SymbolicName: BasicMovieFinder
Bundle-Version: 1.0.0
Bundle-Activator: osgitut.movies.impl.BasicMovieFinderActivator
Import-Package: org.osgi.framework,
 osgitut.movies;version="[1.0.0,2.0.0)"

지난 번에 비해 두가지가 Manifest 에 추가되었습니다. 프레임워크에게 Activator 를 알려주는 Bundle-Activator 부분 ( 지난번엔 이부분은 필요없었죠 ). 그리고 Import-Packageorg.osgi.framework 를 추가했습니다. 지난번 번들에선 프레임워크와 연동할 일이 없었으므로, OSGi API 패키지가 필요없었기 때문입니다.

자 이제 BasicMovieFinder.jar 를 다시 만들수 있습니다.

> javac -classpath equinox.jar;MoviesInterface.jar osgitut/movies/impl/*.java
> jar cfm BasicMovieFinder.jar BasicMovieFinder.mf osgitut/movies/impl/*.class

OSGi 콘솔로 돌아가면, 아마 아직 지난번의 BasicMovieFinder.jar 가 설치되어 있을것입니다. 그럼 OSGi 에게 번들을 업데이트 할수 있도록 update N 을 입력해보세요. N 은 ss 를 입력했을때 보이는 번들의 ID 번호입니다. 자 이제 start N 을 입력해서 번들을 시작해 보세요. 그런데.. 별로 일어난 일이 없습니다.

실제로 우린 우리의 첫번째 서비스를 OSGi 서비스 레지스트리에 등록했습니다만, 불행하게도 아직 이 서비스의 사용자가 없으므로 등록작업이 눈에 보이는 효과를 가져오지 못합니다. 우리의 코드가 정말로 무엇을 했는지 확인하려면 좀더 깊게 들어가야 하므로 아래의 명령을 입력해 봅니다.

services (objectClass=*MovieFinder)

그럼 아래와 같은 출력을 볼수 있습니다.

{osgitut.movies.MovieFinder}={category=misc, service.id=22}
  Registered by bundle: file:BasicMovieFinder.jar [4]
  No bundles using service.

멋집니다. 우리의 서비스가 등록되어 있네요. 이제 어떻게 서비스를 찾고 다른 번들에서 사용하는지를 말해드리고 싶지만, 다음날까지 기다려야 할 듯 합니다. 그동안 services 명령을 가지고 무엇을 할수 있는지 시도해 보세요. 입문자 분들께서는 services 뒤의 괄호안의 문자열을 빼고 입력해 보세요. 그 문자열은 우리가 관심있는 서비스들만 볼 수 있도록 출력될 서비스의 숫자를 줄여주는 필터 입니다. 필터가 없다면 등록된 모든 서비스들을 보실수 있습니다. 놀라울 정도로 많은 서비스가 있을겁니다!

OSGi 시작하기 3 – 번들간의 의존관계 : [번역] Dependencies between bundles

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
* 파트 2 – 프레임워크와 연동하기 : Interacting with the Framework
에 이어 3번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 3 – 번들간의 의존관계

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t90544.html – Getting started with OSGi : Dependencies between bundles

지난회까지의 튜토리얼에서는, 어떻게 번들이 시작되고 중단되는지, 어떻게 프레임워크와 연결되고 다른 번들의 라이프 사이클에 연결되는지를 알아보았습니다. 그런데 정말 번들은 뭘 위한걸까요 ?

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

OSGi 는 이 문제를 매우 우아한 방법으로 해결했습니다. 말로 하는것보다 보여주는 방법이 좋을듯 하니, 코드로 가보도록 합시다. 불행하게도 우린 지금까지 기본 패키지를 이용해 왔지만, 이는 더이상 동작하지 않습니다. 우린 적절한 패키지가 필요하게 될 것입니다. 그럼 아주 간단한 JavaBean 스타일의 클래스로 시작해 봅시다. osgitut/movies/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 파일을 만들고 아래코드를 복사하세요.

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

이제 이 두개의 클래스들을 번들안에 집어 넣습니다. 우리의 번들은 터무니없을 정도로 작고, 거의 쓸모가 없지만 지금은 문제없습니다. 전과 같이 Manifest 파일을 만들어야 하므로, 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 되었으므로, 처음에는 좀 이상하게 보일수 있습니다. 하지만, 혹시 당신의 Jar 파일안에 내부적으로만 볼수 있는 코드를 패키지에 추가하는 것을 원해본적 없으신가요 ? 물론 클래스는 private 나 proctected 로 만들 수 있겠지만, 그러면 당신의 Jar 파일에 있는 다른 Package 에서도 못 보게 됩니다. 그래서 OSGi는 효율적인 새로운 코드 보안 레벨을 소개했습니다. 만약 번들안에 있는 패키지가 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 에 복사합니다.

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 를 아래와 같이 만듭니다.

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,2.0.0)"

다른 번들에 의해 Export 되었던 osgitut.movies 번들을 import 하고 잇다는것을 주목하세요. 또한 이번엔 import 할때 버전의 범위를 추가했습니다. 프레임워크는 import 에 맞는 적절한 export 를 찾을때 이 범위를 사용합니다. OSGi 는 일반적인 수학에서 사용하는 범위형태의 문법을 사용합니다. “[” 는 포함한다는 것을 의미하며, “)”는 포함하지 않는다는 것을 의미합니다. 이에 의해 우리는 버전 “1.x” 대를 효과적으로 선택했습니다.

다시 한번 말씀드리지만, 이 경우에 버전번호 제약을 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 상태에 있다는것을 보실수 있습니다.

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

(NB 여러분의 번들 리스트는 제것과 약간 다르게 보일수 있습니다. 번들의 ID 는 당신이 얼마나 HelloWorld 번들을 설치/삭제 해보았는지에 따라 다를수 있씁니다. 번들ID 를 당신의 것으로 바꿔서 보도록 하세요.).

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

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

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

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

네, 그게 문제였네요. 어떤 번들도 osgitut.movies 패키지를 export 하고 있지 않기 때문에 import 가 불가능합니다. 그럼 이제 MovieInterface.jar를 설치하고 ss 를 입력해 보세요. 리스트는 아래와 같을겁니다.

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 를 입력해 보는것입니다. ss 의 출력은 이제 아래와 같습니다.

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 상태가 된것을 보세요.

여기까지 입니다. Equinox 콘솔과 해볼수 있는 더 많은 재미난 것들을 위해서는 IBM developerWorks에 있는 Chris Aniszczyk 의 훌륭한 기사 를 참고하세요. 다음회에서는 OSGi 서비스에 대해 알아보겠습니다. 기대해 주세요.

OSGi 시작하기 2 – 프레임워크와 연동하기 : [번역] Interacting with the Framework

이 글은 Neil Bartlett 의 연재 글 “Getting started with OSGi”의 번역본입니다.
* 파트 1 – 첫번째 번들 : Your First Bundle
에 이어 2번째 글입니다. 오탈자 및 이상한 번역은 댓글로 남겨주세요

Getting started with OSGi : OSGi 시작하기 파트 2 – 프레임워크와 연동하기

저자 : Neil Bartlett < njbartlett at gmail dot com >
역자 : 권 정혁 < guruguru at gmail dot com >

글원본 : http://www.eclipsezone.com/eclipse/forums/t90448.html – Getting started with OSGi : Interacting with the Framework

EclipseZone OSGi 튜토리얼에 돌아오신걸 환영합니다.

지난 시간에는 시작과 종료시에 메시지를 출력하는 Hello World 번들을 살펴보았는데요. 이것은 BundleActivator 인터페이스를 구현하여 start 와 stop 메소드를 제공함으로서 가능했습니다. 코드를 다른부분을 살펴보면, startstop 메소드의 서명(Method Signature , 역주:메소드의 이름,인자,리턴타입등을 총칭)에서 우리가 BundleContext 라는 인자를 받았다는 것을 알수 있습니다. 이 장의 튜토리얼에서 우린 BundleContext 에 대해 알아보고 이 것으로 무엇을 할수 있는지 살펴볼 것 입니다.

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

지난번에 시작한 Equinox 가 아직 실행중이라면 재시작 할 필요는 없습니다. 실행중이 아니라면, 다시 시작하기 위한 커맨드는

> java -jar equinox.jar -console

입니다. ss 를 입력해서 지난번에 설치한 HelloWorld 번들이 아직 설치되어 있는지 확인해 볼 수 있습니다. 당신이 Equinox 를 종료하고 재 시작했더라도 그것을 볼수 있는데, 왜냐하면 OSGi 프레임워크가 각각의 실행간에 실행 상태를 저장하기 때문입니다.

이번 연습을 위해, 우리는 Hello World 번들을 찾아내고 Uninstall(제거) 하는 번들을 작성해 볼것입니다. 물론 콘솔창에서 uninstall 명령을 내림으로 간단히 해볼수 있지만, 어떻게 OSGi API 를 통해 프로그램적으로 실행가능한지를 알아보기 위해서입니다. 그럼 HelloWorldKiller.java 라는 파일을 만들고 아래의 코드를 복사합니다.

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

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

Manifest-Version: 1.0
Bundle-Name: HelloWorldKiller
Bundle-Activator: HelloWorldKiller
Bundle-SymbolicName: HelloWorldKiller
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 콘솔에서 install file:HelloWorldKiller.jar 를 입력해서 새 번들을 설치하고 ss 를 입력해 봅니다. 상태 리스트는 이제 아래와 같을 것입니다.

id      State       Bundle
0       ACTIVE      system.bundle_3.2.1.R32x_v20060919
1       ACTIVE      HelloWorld_1.0.0
2       INSTALLED   HelloWorldKiller_1.0.0

자 이제 start 2 를 입력해서 Hello World Killer 를 실행하여 봅시다. 아래와 같은 출력을 보실수 있습니다.

HelloWorldKiller searching...
Hello World found, destroying!
Goodbye EclipseZone Readers!

마지막 출력라인은 원본 Hello World 번들에서 나왔다는것을 주목하세요. 우리가 Killer 를 실행하기 전에 그 번들이 active 상태였기 때문에 제거되기 전에 중단 되어야만 했기에 BundleActivator 에게 stop 이 실행 되도록 만들었습니다.

ss 의 출력을 보도록 합시다. Hello World 가 이제 사라졌습니다.

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

여기에 보안 문제가 있지 않은가 궁금해 하실수 있습니다. 어떤 번들이라도 다른 어떤 번들을 제거할수 있는것 처럼 보이니까요! 다행스럽게도 OSGi 는 프레임워크와의 모든 연계작업들을 세밀하게 조종할수 있는 종합적인 보안계층을 가지고 있습니다. 예를 들어 특정 “관리” 번들에 의해서만 번들이 제거될수 있도록 할수도 있습니다. 어쨋거나, 보안이 동작하도록 하는것은 대부분 설정(Configuration)상의 문제이니 이 연재에서는 우리는 코드에만 집중하기로 합니다.

이번회는 여기까지 입니다. 다음시간까지 BundleContext 의 인터페이스를 살펴보고 무엇을 할수 있는지 알아보는건 어떨까요 ? 예를 들어 installBundle 메소드를 이용하여 프로그램적으로 새 번들을 설치하는 것을 시도해 볼 수도 있습니다. 또는 현재 설치된 번들들에 대한 리스트를 가져와서 마지막으로 수정된 날짜와 시간을 출력하도록 할 수도 있구요. 시작하는데 도움이 필요하다면 OSGi Release 4 API 에 대한 JavaDoc 을 참고하세요