화이트보드 패턴 예제 원문: http://www.osgi.org/wiki/uploads/Links/whiteboard.pdf
OSGi 환경에서 주요 문제은 번들간의 종속성을 처리하는 것이다: 다른 번들이 소유하거나 생성한 객체를 레퍼런스하는 번들간의 종속성. 이문제는 다음과 같은 간단한 예제를 사용하여 설명할 수 있다.
이 예제는 수 많은 스크린들을 회전하는 LCD 디스플레이 서비스로 구성되어 있다. 스크린들의 내용은 ContentProvider들에 의해 제공된다. 이것은 LCD에 표시되길 원하는 번들에 의해 구현될 수 있는 인터페이스이다. ContentProvider들은 새로운 스크린이 필요하면 그들의 내용에 대해 질의된다.[내용을 얻어오는 함수를 가지고 있다.] 이 예제에서, DisplayManager는 레지스트리에서 모든 스크린들의 회전에 대해 책임이 있고 ContentProvider를 구현한 ClockManager는 현재 시간을 전달한다. (System.out을 사용)
이 예제의 초점은 DisplayManager와 ClockManager간 종속성을 어떻게 처리하는지 보여주는데 있다. OSGi 환경에서 DisplayManger와 ClockManager는 언제든지 오고 갈 수 있다. [생성/삭제 될 수 있다.]
DisplayManager는 등록된 모든 ContentProvider를 가져와서 회전해야한다는 의미같다.
결국 ContentProvider가 등록/제거 되는것을 계속 관리해야한다는 의미~ ContentProvider가 Listener의 역할 수행
Listener 패턴을 사용하여 이를 구현하는 과정은 다음과 같다.
Display 객체를 추적하는 ContentProvider를 사용한다. (ContentProvider는 어느 Display에 등록되었는지 알아야한다는 의미인듯;)
DisplayManager는 각 ContentProvider를 등록하고 회전한다.
package org.osgi.example.display;
public interface Display {
//Display와 연관되어 있는 ContentProvider를 관리하기 위한 add/remove 함수 필요
void addContentProvider( ContentProvider p );
void removeContentProvider( ContentProvider p );
}
package org.osgi.example.display;public interface ContentProvider {
String getContent();
}
ContentProvider를 구현한 ClockManager을 살펴보면 다음과 같다.
ClockManager는 DisplayTrack를 가지고 레지스트리에 Display 서비스들을 추적한다.
OSGi에 새로운 Display서비스가 등록되면, Clock객체는 ContentProvider로써 생성되고 등록된다.
//Clock(ContentProvider)를 자신을 방금 등록된 Display의 Content로 등록한다.
class Clock implements ContentProvider {
Display display;
Clock( Display display ) {
this.display = display;
display.addContentProvider( this );
}
void dispose() {
display.removeContentProvider(this);
}
public String getContent() { return new Date().toString(); }
}
//ServiceTracker는 서비스 레지스트리로 부터 서비스의 상태를 모니터링하는데 사용된다.
//addingService - ServiceTracker에 추가되기 전에 호출됨
//modifiedService - 서비스의 properties가 변경되었을 때 호출됨
//removedService - 더이상 ServiceTracker에 의해 추적되지 않을때 호출됨
class DisplayTracker extends ServiceTracker {
DisplayTracker( BundleContext context ) {
super( context, Display.class.getName(), null );
}
//Display 서비스가 등록되면 새로운 Clock객체를 생성하고 Display 레퍼런스를 연관시킨다.
public Object addingService( ServiceReference ref ) {
Display display = (Display) context.getService(ref);
return new Clock( display );
}
//Clock 객체를 제거하고 Display 서비스를 서비스레지스트리에서 제거한다.
public void removedService( ServiceReference ref, Object obj ) {
Clock clock = (Clock) obj;
clock.dispose();
context.ungetService( ref );
}
}
public class ClockManager implements BundleActivator {
DisplayTracker tracker;
public void start( BundleContext context ) {
tracker = new DisplayTracker( context );
tracker.open();
}
public void stop( BundleContext context ) {
tracker.close();
}
}
다음은 DisplayManager 구현~
Display 서비스는 ServiceFactory를 사용하여 구현되어야 한다.
//Display는 ContentProvider를 Vector로 관리한다.
class ContentProviderRegistration implements Display {
Vector providers;
DisplayManager manager;
ContentProviderRegistration( DisplayManager manager ) {
this.manager = manager;
providers = new Vector();
}
public synchronized void addContentProvider( ContentProvider p ) {
providers.addElement( p );
manager.addContentProvider(p);
}
public synchronized void removeContentProvider( ContentProvider p ) {
if ( providers.indexOf( p ) >= 0 ) {
providers.removeElement( p );
manager.removeContentProvider( p );
}
}
void synchronized dispose() {
for ( Enumeration e= providers.elements(); e.hasMoreElements(); ) {
ContentProvider p = (ContentProvider) e.nextElement();
manager.removeContentProvider( p );
}
providers = null;
}
}
//DriverManager가 커스텀된 서비스 객체를 얻기 위해서
class DisplayFactory implements ServiceFactory {
DisplayManager manager;
DisplayFactory( DisplayManager manager ) {
this.manager = manager;
}
public Object getService( Bundle b, ServiceRegistration r ) {
return new ContentProviderRegistration( manager );
}
public void ungetService( Bundle b, ServiceRegistration r, Object s ) {
ContentProviderRegistration cpr = (ContentProviderRegistration) s;
cpr.dispose();
}
}
public class DisplayManager implements BundleActivator, Runnable {
Thread thread;
ServiceRegistration registration;
DisplayFactory factory;
Vector providers;
public void start( BundleContext context ) {
providers = new Vector();
factory = new DisplayFactory( this );
registration = context.registerService(
Display.class.getName(),
factory,
null
);
thread = new Thread( this, "DisplayManager Listener" );
thread.start();
}
public void stop( BundleContext context ) {
thread = null;
}
public synchronized void run() {
Thread current = Thread.currentThread();
int n = 0;
while ( current == thread ) {
if ( providers.size() != 0 ) {
if ( n >= providers.size() )
n = 0;
ContentProvider cp = (ContentProvider) providers.elementAt(n++);
System.out.println( "LISTENER: " + cp.getContent() );
}
try { wait( 5000 ); } catch( InterruptedException e ) {}
}
}
void addContentProvider( ContentProvider p ) {
providers.addElement(p);
}
void removeContentProvider( ContentProvider p ) {
providers.removeElement(p);
}
}
Whiteboard 구현
Display 객체를 추적하고 Display를 가진 ContentProvider를 등록 하는 대신 화이트보드 접근은 OSGi 프레임워크의 서비스로써 ContentProvider를 등록하기만 하면된다. 그리고 ServiceTracker를 이용해서 Listener 호출~~
public interface ContentProvider {
String getContent();
}
class Clock implements ContentProvider {
Clock( ) {}
public String getContent() { return new Date().toString(); }
}
public class ClockManager implements BundleActivator {
ServiceRegistration registration;
Clock clock = new Clock();
public void start( BundleContext context ) {
registration = context.registerService(
ContentProvider.class.getName(),
clock,
null );
}
public void stop( BundleContext context ) {}
}
public class DisplayManager implements BundleActivator, Runnable {
Thread thread;
ServiceTracker tracker;
public void start( BundleContext context ) {
tracker = new ServiceTracker( context, ContentProvider.class.getName(), null );
tracker.open();
thread = new Thread( this, "DisplayManager Whiteboard" );
thread.start();
}
public void stop( BundleContext context ) {
tracker.close();
thread = null;
}
public synchronized void run() {
Thread current = Thread.currentThread();
int n = 0;
while ( current == thread ) {
Object [] providers = tracker.getServices();
if ( providers != null && providers.length > 0 ) {
if ( n >= providers.length )
n = 0;
ContentProvider cp = (ContentProvider) providers[n++];
System.out.println( "WHITEBOARD " + cp.getContent() );
}
try { wait( 5000 ); } catch( InterruptedException e ) {}
}
}
}