이 글은 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 >
마지막 글에서 우린 어떻게 서비스를 등록하는지에 대해 알아봤습니다. 이제 그 서비스를 찾고 다른 번들에서 그 서비스를 이용하도록 작업해 봅시다.
Martin Folwer의 Dependency Injection에 대한 글에서 영감을 얻었던 지난번처럼, 우린 문제를 우리 요구사항에 포함할 것입니니다. 우린 MovieFinder
를 서비스로 만들어서 서비스 레지스트리에 등록했습니다. 이젠 특정 감독에 의해 제작된 영화를 찾기위해 MovieFinder
를 사용하는 MovieLister
를 만들어볼 것입니다. 그리고 이 MovieLister
자체도 GUI Application 같은 다른 특정번들에 사용되는 서비스가 될것이라고 가정합니다. 여기서 문제점은, OSGi 서비스는 동적이라 등록되고 사라지고 한다는것입니다. 즉, 우리가 MovieFinder
서비스를 호출하고자 할때, 서비스가 불가능할수도 있습니다.
그러면, 만약 MovieFinder
서비스가 존재하지 않는다면 MovieLister
는 무엇을 해야할까요 ? MovieLister
가 하는 일중에 MovieFinder
에 대한 호출이 매우 중요한 것이라는것은 확실하므로, 우리가 선택할수 있는 옵션은 몇가지 밖에 없습니다.
- 에러를 낸다. (Null 을 리턴하거나 Exception 을 내거나)
- 기다린다.
- 처음부터 아예 설치가 안되도록 한다. ( 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
를 사용하지 않고 레지스트리에서 서비스를 가져오는 코드를 보셨을수도 있습니다. 예를 들어 BundleContext
에 getServiceReferece
와 getService
를 호출하는것이 가능합니다. 하지만 이런 코드는 꽤 복잡하고 사용후 잘 지우도록 조심해야 합니다. 제 의견으론, 로우레벨 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-Activator
에 osgitut.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()
메소드 대신 ServiceTracker
의 waitForService(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” 에 대해 알아보겠습니다. 기대해 주세요!