私は Equinox OSGi フレームワークで Java アプリケーションを構築しており、DS (宣言型サービス) を使用して、参照および提供されるサービスを宣言しています。これまで実装してきたすべてのサービス コンシューマーはたまたまサービス プロバイダーでもあったため、(1 つのコンシューマーにアタッチするのではなく、複数のコンシューマーが再利用できるように) それらをステートレスにするのは当然のことでした。フレームワークによってインスタンス化されます(デフォルトのコンストラクター、私のコードのどこにも呼び出されません)。
MyClass
ここで、別の状況があります。サービスを参照するクラスがありますMyService
が、それ自体はサービス プロバイダーではありません。MyClass
OSGi フレームワークにインスタンス化させるのではなく、自分自身をインスタンス化できる必要があります。次に、フレームワークが既存のMyService
インスタンスをインスタンスに渡すようにしMyClass
ます。このようなもの:
public class MyClass {
private String myString;
private int myInt;
private MyService myService;
public MyClass(String myString, int myInt) {
this.myString = myString;
this.myInt= myInt;
}
// bind
private void setMyService(MyService myService) {
this.myService = myService;
}
// unbind
private void unsetMyService(MyService myService) {
this.myService = null;
}
public void doStuff() {
if (myService != null) {
myService.doTheStuff();
} else {
// Some fallback mechanism
}
}
}
public class AnotherClass {
public void doSomething(String myString, int myInt) {
MyClass myClass = new MyClass(myString, myInt);
// At this point I would want the OSGi framework to invoke
// the setMyService method of myClass with an instance of
// MyService, if available.
myClass.doStuff();
}
}
私の最初の試みは、DS を使用してコンポーネント定義を作成し、そこからMyClass
参照することでした。MyService
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="My Class">
<implementation class="my.package.MyClass"/>
<reference bind="setMyService" cardinality="0..1" interface="my.other.package.MyService" name="MyService" policy="static" unbind="unsetMyService"/>
</scr:component>
ただし、MyClass
ライフサイクルを管理したくないため、実際にはコンポーネントではありません。インスタンス化を自分で処理したいのです。ニール・バートレットがここで指摘しているように:
たとえば、コンポーネントが特定のサービスに「依存している」と言うことができます。この場合、コンポーネントはそのサービスが利用可能な場合にのみ作成およびアクティブ化され、サービスが利用できなくなると破棄されます。
これは私が望むものではありません。ライフサイクル管理のないバインディングが必要です。[注: カーディナリティを0..1
(オプションで単項) に設定しても、フレームワークはインスタンス化を試みますMyClass
(引数のないコンストラクタがないため失敗します)]
それで、私の質問: DS を使用して、私が探しているこの「バインドのみ、ライフサイクル管理なし」の機能を使用する方法はありますか? DS でこれが不可能な場合、代替手段は何ですか? また、何をお勧めしますか?
更新: 使用ServiceTracker
(Neil Bartlett が提案)
重要: 回答として、これの改良版を以下に投稿しました。私はこれを「歴史的」な目的のためにここに置いています。
ServiceTracker
この場合の申請方法がわかりません。以下に示すように、静的レジストリを使用しますか?
public class Activator implements BundleActivator {
private ServiceTracker<MyService, MyService> tracker;
@Override
public void start(BundleContext bundleContext) throws Exception {
MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bundleContext);
tracker = new ServiceTracker<MyService, MyService>(bundleContext, MyService.class, customizer);
tracker.open();
}
@Override
public void stop(BundleContext bundleContext) throws Exception {
tracker.close();
}
}
public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer<MyService, MyService> {
private BundleContext bundleContext;
public MyServiceTrackerCustomizer(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
@Override
public MyService addingService(ServiceReference<MyService> reference) {
MyService myService = bundleContext.getService(reference);
MyServiceRegistry.register(myService); // any better suggestion?
return myService;
}
@Override
public void modifiedService(ServiceReference<MyService> reference, MyService service) {
}
@Override
public void removedService(ServiceReference<MyService> reference, MyService service) {
bundleContext.ungetService(reference);
MyServiceRegistry.unregister(service); // any better suggestion?
}
}
public class MyServiceRegistry {
// I'm not sure about using a Set here... What if the MyService instances
// don't have proper equals and hashCode methods? But I need some way to
// compare services in isActive(MyService). Should I just express this
// need to implement equals and hashCode in the javadoc of the MyService
// interface? And if MyService is not defined by me, but is 3rd-party?
private static Set<MyService> myServices = new HashSet<MyService>();
public static void register(MyService service) {
myServices.add(service);
}
public static void unregister(MyService service) {
myServices.remove(service);
}
public static MyService getService() {
// Return whatever service the iterator returns first.
for (MyService service : myServices) {
return service;
}
return null;
}
public static boolean isActive(MyService service) {
return myServices.contains(service);
}
}
public class MyClass {
private String myString;
private int myInt;
private MyService myService;
public MyClass(String myString, int myInt) {
this.myString = myString;
this.myInt= myInt;
}
public void doStuff() {
// There's a race condition here: what if the service becomes
// inactive after I get it?
MyService myService = getMyService();
if (myService != null) {
myService.doTheStuff();
} else {
// Some fallback mechanism
}
}
protected MyService getMyService() {
if (myService != null && !MyServiceRegistry.isActive(myService)) {
myService = null;
}
if (myService == null) {
myService = MyServiceRegistry.getService();
}
return myService;
}
}
これはあなたがそれを行う方法ですか?そして、上記のコメントで私が書いた質問についてコメントしていただけますか? あれは:
Set
サービスの実装が適切に実装されていない場合の問題equals
とhashCode
.- 競合状態:チェック後にサービスが非アクティブになる可能性があります。
isActive