정보공간_1

[2기 강북 송석호]OSGi 동적으로 서비스 추적하기 본문

IT 놀이터/Elite Member Tech & Talk

[2기 강북 송석호]OSGi 동적으로 서비스 추적하기

알 수 없는 사용자 2012. 11. 28. 02:13

안녕하세요.

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

이번 달에는 equinox 기반으로 OSGi 서비스를 동적으로 추척하는 법을 살펴보겠습니다.

 

1. 동적으로 서비스 추적하기

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

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

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

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

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

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

MovieListerImpl.java

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

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 라는 새로운 파일을 아래와 같이 작성합니다.

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 를 필요로 합니다.  편리하게도 슈퍼클래스인 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 입니다.

 

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 입니다.

 

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)"
---------------------------------------------------------------------------------------------------------------
그 후에는 아래 단계들을 따라해서 모든것이 동작하는지를 살펴보세요.

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


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

한편으로 작성한 코드에는 약간의 문제가 있습니다.  MovieFinderTrackerTrackingMovieListerActivator클래스는 반복사용 되는 문장들이 가득해서, 우리가 시스템을 확장하기 시작한다면 우리는 같은 코드를 매번 조금씩만 바꿔서 작성하게 되어 매우 피곤해 질것입니다.

 

2. 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