2016-06-21 13 views
4

Ich habe eine OSGi-Komponente MyComponent.Wie können Referenzen in einer OSGi-Komponente dynamisch aktualisiert werden?

Diese Komponente bezieht sich auf einen Service MyService. Jetzt MyService hat ein paar Implementierungen MyServiceImpl1 und MyServiceImpl2. MyComponent hat auch Eigentum MyProperty.

Jetzt was ich will ist, dass immer wenn MyProperty ist 1, MyComponent.MyService bindet an MyServiceImpl1. Und wenn ich zu 2 ändere, aktualisiert MyComponent.MyService dynamisch die MyService Bindung an `MyServiceImpl2.

Wie kann ich dies erreichen? Als Referenz benutze ich Apache Felix Container und würde es vorziehen, OSGi Apis niedrigerer Ebene zu vermeiden.

Antwort

1

Zuerst müssen Sie Ihre Implementierungen mit verschiedenen Filtern veröffentlichen, um die eine oder andere durch Filterung zu erhalten. Dann können Sie die ServiceReference immer ändern, wenn sich die Eigenschaft mit bundleContext.getServiceReferences() ändert.

Das technische Detail hängt vom verwendeten Framework ab (DS, iPojo, none, ...).

+0

Danke Alexandre. Ich benutze Apache Felix Framework. Ich habe die Frage mit diesen Informationen aktualisiert. – sdm

+0

Apache Felix ist eine OSGi-Framework-Implementierung, aber wie veröffentlichen Sie Ihre Komponente? Verwenden einer Activator- und registerService-Methode, durch Annotation, ...? –

+0

Btw, ich denke nicht, dass dies ohne die Verwendung von "Low-Level" API möglich ist –

2

Ich gehe davon aus Implementierung von MyService abgefragt werden kann seinen Typen (zB unten) zu berichten:

public interface MyService { 
    public static final String TYPE = "myservice.type"; 
} 

Wenn ja, für eine deklarativen Dienst OSGi-Komponente auf Apache Felix, hier ist ein Ansatz:

  • halten MyService Referenz in MyComponent mit
    • Dynamic Referenzrichtlinie (policy = ReferencePolicy.DYNAMIC)
    • 1..n Kardinalität (cardinality = ReferenceCardinality.MANDATORY_MULTIPLE)
  • bind/unbind Methoden zur MyService Referenz in MyComponent
  • installieren ein Modified Methode in MyComponent

bind/unbind Methoden der MyComponent wird aufgerufen, wie und wann MyService Implementierungen installieren werden von Felix SCR instanziiert. Sie möchten eine Karte verfügbarer Implementierungen verwalten.

Modified Methode wird aufgerufen, wenn ein Konfigurations-Update-Ereignis für MyComponent vorliegt. In dieser Methode kann basierend auf der aktualisierten Komponentenkonfigurationseigenschaft eine geeignete Methode für die weitere Verarbeitung ausgewählt werden.

So würde die Komponente aussehen, wenn Felix SCR annotations verwendet wird.

@Component (metatype = true, immediate = true) 
@Service (value = MyComponent.class) 
public class MyComponent { 
    @Property(name = "impl.selector", value = "impl_1") 
    private String implSelector = "impl_1"; 

    @Reference(
     referenceInterface = MyService.class, 
     policy = ReferencePolicy.DYNAMIC, 
     cardinality = ReferenceCardinality.MANDATORY_MULTIPLE, 
     strategy = ReferenceStrategy.EVENT, 
     bind = "bindService", 
     unbind = "unbindService" 
    ) 

    private Map<String, MyService> availableMyServiceImpls = new HashMap<String, MyService>(); 
    private MyService service = null; 

    @Activate 
    public void activate(ComponentContext componentContext) { 
     service = availableMyServiceImpls.get(implSelector); 
    } 

    @Deactivate 
    public void deactivate(ComponentContext componentContext) { 
     availableMyServiceImpls.clear(); 
    } 

    public void bindService(MyService serviceRef, Map<?,?> refProperties) { 
     String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY); 
     availableMyServiceImpls.put(serviceImplName, serviceRef); 
    } 

    public void unbindService(MyService serviceRef, Map<?,?> refProperties) { 
     String serviceImplName = (String) refProperties.get(MyService.NAME_PROPERTY); 
     availableMyServiceImpls.remove(serviceImplName); 
    } 

    @Modified 
    public void modified(ComponentContext componentContext) { 
     Dictionary<String, Object> componentProps = componentContext.getProperties(); 
     implSelector = PropertiesUtil.toString(componentProps.get("impl.selector"), ""); 
     service = availableMyServiceImpls.get(implSelector); 
    } 
} 
+0

wechseln Dies ist ein guter Ansatz, wenn die Eigenschaft durch Konfiguration geändert wird, aber wenn es sich um eine Methode handelt, die eine "setProperty" Methode aufruft? Man kann "' '' '' '' 'in der' 'setProperty'' Methode immer aufrufen, es wirkt etwas komisch, aber es sollte funktionieren. –

+0

Nicht sicher, ich folge völlig, @AlexandreCartapanis. Nach Aktualisierung der 'Eigenschaft' von' MyComponent' oben über [Felix ''ConfigurationAdmin'-Schnittstelle] (http://felix.apache.org/apidocs/configadmin/1.2.4/org/osgi/service/cm/ConfigurationAdmin. html), nämlich 'Konfiguration config = configAdmin.getConfiguration (COMPONENT_PID)' ' config.update (new Hashtable () {{put ("impl.selector", "impl_4");}});' Die Methode mit der Bezeichnung '@ Modified' wurde aufgerufen. Kannst du mir bitte helfen, das 'setProperty' Szenario zu verstehen, auf das du anspielst? –

+0

Wenn Sie die '' property'' nicht über Felix ConfigurationAdmin, sondern direkt über code (ein '' property = XXX'' oder ein '' setProperty (XXX) '' 'aktualisieren, wird die Methode mit' '@ Modified'' kommentiert nicht genannt werden –

5

Der einfachste Weg, Abhängigkeiten zu konfigurieren, ist mit der '.target' Eigenschaft. Dies erfordert, dass sich die Implementierungen mit einer identifizierenden Eigenschaft registrieren, z. B. impl=1. und impl=2.

@Component(property="impl=1") 
public class MyServiceImpl1 implements MyService { 
} 
@Component(property="impl=2") 
public class MyServiceImpl2 implements MyService { 
} 

Die Komponente dann aussehen könnte:

@Component 
public class MyComponent { 
    @Reference(target="(impl=1)") 
    volatile MyService myService; 
} 

In diesem Fall, dass Sie nicht in der Lage sein, 1 oder 2 als Flag zu verwenden, aber Sie würden die Konfigurationseigenschaft für MyComponent mit der ändern müssen Name myService.target mit einem anderen Filter. (Dies wird mit standardisierten OSGi-Annotationen angezeigt.)

Wenn Sie auf einer Eigenschaft bestehen, die 1 oder 2 ist (nennen wir es select) für MyComponent dann ist es aufwendiger. Zuerst haben wir das Problem, zufrieden zu sein. Sollte MyComponent zufrieden sein, wenn es gemäß der ausgewählten Eigenschaft impl 1 benötigt wird, aber nur 2 verfügbar ist? Wenn das in Ordnung ist dann die folgende viel kompliziertere Lösung

@Designate(ocd=Config.class) 
@Component(property = "select=1") 
public class MyComponent { 
     static Class<?> types [] = { 
      MyServiceImpl1.class, 
      MyServiceImpl2.class, 
     }; 
     @interface Config { 
      int select() default 1; 
     } 

     @Reference(target="(|(impl=1)(impl=2))") 
     volatile List<MyService> candidates; 

     volatile MyService selected; 

     @Activate 
     @Modify 
     void changed(Config config) { 
      Class<?> type = types[config.select()]; 
      Optional<MyService> service = candidates. 
       stream(). 
       filter(type::isInstance). 
       findFirst(); 
      this.service = service.isPresent() ? service.get() : null; 
     } 
} 

arbeiten sollten Sie sehen es ist generell eine schlechte Idee für eine Komponente zu starten ihre eigenen Abhängigkeiten zu behandeln. Ich bin daher neugierig auf dein Szenario in der realen Welt.

Ich finde es immer sehr umständlich und schwierig, Designs zu pflegen, bei denen Komponenten wählerisch sind, auf wen sie sich beziehen. Es ist manchmal unvermeidlich, aber in allgemeinen Lösungen, wo MyServiceImpl2 und MyServiceImpl1 entscheiden, zu registrieren oder nicht basierend auf einer bestimmten Bedingung die Realität besser widerspiegeln.

Also die große Frage, die ich habe, was die 1 oder 2 Eigenschaft in der realen Welt widerspiegelt? Kann dies nicht als Serviceabhängigkeit modelliert werden?

(Haftungsausschluss: Code nicht getestet und keine Fehlerbehandlung darin)

+0

Danke Peter Kriens für eine sehr aufschlussreiche Antwort wie immer! Lassen Sie mich meinen Anwendungsfall erklären. 'MyComponent' ist im Grunde ein Token-Fetcher, der mit einem externen REST-Dienst kommuniziert, um ein Token zu holen REST-Dienst Die 'MyServiceImpl's sind im Grunde Anforderungszuordner, die verschiedenen Varianten des REST-Dienstes entsprechen.Ich muss einen Mechanismus bereitstellen, damit' MyComponent' den geeigneten Dienstimpl auswählt. – sdm

+0

Die Frage ist, was das Kriterium ist? Wer setzt die 0 oder 1? –

+0

Dies wird normalerweise vom Systemadministrator festgelegt, der die verwendete REST-Dienstvariante kennt und das System entsprechend konfiguriert. Außerdem kann 'MyComponent' möglicherweise mehrere Instanzen haben, da es sich um eine Konfigurationsfactory handelt. In einem solchen Szenario verwendet jede MyComponent-Instanz die entsprechende Dienstimplementierung in Abhängigkeit von ihrer individuellen Konfiguration. – sdm

Verwandte Themen