Is it valid to deactivate a component in OSGi manually if I am using Declarative Services?
For example, let's say I have a component, with the implementation:
//component
class Overseer(){
List<ServiceReference> serviceRefs = ...//populate
private void doStuff(){
serviceRef = serviceRefs[i];
if(dontNeedThisAnymore){
serviceRefs.remove(serviceRef);
serviceRef.getBundle().stop();
}
}
The best way to do this is from another component in the same bundle, using the ComponentContext API.
You can write a component so that it takes ComponentContext as a param to its activate method. That interface has enableComponent and disableComponent methods that can be used to enable/disable other components in the same bundle.
I call this a "gatekeeper" component because it can be used to setup resources needed by the other components before enabling them. For example, you may have multiple components that need a database to be started up before they can do their job... the gatekeeper would take care of starting the database and then call enableComponent(null) to enable the other components. Similarly if the gatekeeper could detect that the database has shutdown and at that point disable the other components. In order for this to work, all components in the bundle except for the gatekeeper need to be set initially to enabled="false".
Related
I've spent some time experimenting with and studying the OSGi enRoute site. The Quick Start, and Base tutorials were really good. Now as a learning exercise, I'm creating my own example following the principles in those tutorials.
I've decided to reproduce the StageService from the blog post "Making JavaFX better with OSGi". Rather than using the org.apache.felix.dm and org.apache.felix.dm.annotation.api packages I want to use the OSGi standard SCR packages (org.osgi.service.component.*) along with the enRoute provider template.
So far everything has worked out nicely. But I'm stuck on one point. In the "Making JavaFX better with OSGi" tutorial the service is programmatically registered into the service registry using the org.apache.felix.dm.DependencyManager like this:
#Override
public void start(Stage primaryStage) throws Exception {
BundleContext bc = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
DependencyManager dm = new DependencyManager(bc);
dm.add(dm.createComponent()
.setInterface(StageService.class.getName(), null)
.setImplementation(new StageServiceImpl(primaryStage)));
}
My assumption is that in this example the DependencyManager is an Apache Felix specific feature rather than an OSGi standard. I would like to have my enRoute provider depend only on OSGi standard features.
So my question is simply:
How would one register a service in the service registry programmatically using only OSGi standard features? (I know from following the enRoute tutorials that if my component implements the exported service that SCR will automatically register my component in the service registry when my component is activated. The problem with this solution though is that when my component is activated it has to launch the JavaFX application in a different thread so as to not block the thread in use by the SCR until the JavaFX application terminates. Because of this, my component must programmatically register the service in the service registry. Otherwise it won't be guaranteed to be available upon registration.)
For reference, here is what I currently have:
private void registerService(Stage stage) {
DependencyManager dm = new DependencyManager(bundle().getBundleContext());
dm.add(
dm.createComponent()
.setInterface(StageService.class.getName(), null)
.setImplementation(new StageServiceImpl(primaryStage))
);
}
But instead I want to replace it with this:
private void registerService(Stage stage) {
// How to register service in service registry using only OSGi standard features? (not the apache felix dependency manager)
}
UPDATE 1
Following BJ Hargrave's recommendation I tried to register the service directly from the bundle context as follows:
FrameworkUtil
.getBundle(getClass())
.getBundleContext()
.registerService(StageService.class, new StageServiceImpl(primaryStage), null);
After doing this and trying to resolve the enRoute application project the following error occurs:
org.osgi.service.resolver.ResolutionException: Unable to resolve
<> version=null: missing requirement
com.github.axiopisty.osgi.javafx.launcher.application
-> Unable to resolve com.github.axiopisty.osgi.javafx.launcher.application
version=1.0.0.201608172037: missing requirement
objectClass=com.github.axiopisty.osgi.javafx.launcher.api.StageService]
I have uploaded the project to github so you can reproduce the error.
Update 2
The build tab in the bnd.bnd file in the provider module shows the following warning:
The servicefactory:=true directive is set but no service is provided, ignoring it
Might this have something to do with the application module not being able to be resolved?
In rare cases it is necessary to register a 'service by hand' using the standard OSGi API. Try very hard to avoid this case because if you start to register (and maybe depend) on services that you manually register you get a lot of responsibility that is normally hidden from view. For example, you have to ensure that the services you register are also unregistered.
One of the rare cases where this is necessary is when you have to wait for a condition before you can register your service. For example, you need to poll a piece of hardware before you register a service for the device. You will need to control the CPU but at that moment you cannot yet register a service. In that case you create an immediate component and register the service manually.
To register a service manually you require a BundleContext object. You can get that objectvia the activate method, just declare a Bundle Context in its arguments and it is automatically injected:
#Activate
void activate( BundleContext context) {
this.context = context;
}
You can now register a service with the bundle context:
void register(MyService service) {
Hashtable<String,Object> properties = new Hashtable<>();
properties.put("foo", "bar");
this.registration = context.registerService( MyService.class, service, properties );
}
However, you now have the responsibility to unregister this service in your deactivate. If you do not clean up this service then your component might be deactivated while your service still floats around. Your service is unmanaged. (Although when the bundle is stopped it will be cleaned up.)
#Deactivate
void deactivate() {
if ( this.registration != null)
this.registration.unregister();
}
If you create the service is a call back or background thread then you obviously have to handle the concurrency issues. You must ensure that there is no race condition that you register a service while the deactivate method has finished.
This text has also been added to the DS Page of OSGi enRoute
Reading the OSGi spec would help you understand the service API.
But this should do it:
ServiceRegistration<StageService> reg = bc.registerService(StageService.class, new StageServiceImpl(primaryStage), null);
I have an example relating my problem. (files joint: https://drive.google.com/file/d/0B8ThLrV6-uchaFlTZTNGQ1FnT1E/view?usp=sharing )
I have 3 ipojo components (3 bunbles):
CallHello uses a DelayService service which implemented in both HelloDelay or HelloComponentReplace
HelloDelay and HelloComponentReplace use a HelloService service which implemented in HelloPrint.
At deployment, I deploy 5 bundles:
service.hello.service.jar
printer.hello.printer.jar
delay.hello.delay.jar
replace.hello.replace.jar
call.hello.call.jar
Result: DelayService uses always the implementation in HelloDelay.
Finally, I run Main.java to control manually selection between HelloDelay and HelloComponentReplace.
I implemented a function to start/stop or uninstall/install bundles in Main.java (and it works well). However, either HelloDelay or HelloComponentReplace is valid.
In the case both is active and valid, i read on the iPOJO website and I can use “comparator”. But I don’t understand how to apply ‘comparator’ to control selection between 2 components above. Is this to change priority? I know that we can change priority of bundle but I cannot know how to apply to my file (Main.java) and iPOJO.
Could we control connection (binding) between a requiring component and many providing components (same service or interface)?
I hope that you could help my difficulty in this time.
Best regards,
You can manipulate the service binding using interceptors: http://felix.apache.org/documentation/subprojects/apache-felix-ipojo/apache-felix-ipojo-userguide/ipojo-advanced-topics/service-binding-interceptors.html
With interceptors, you can hide services, and / or sort the service providers in order to enforce the provider you want to use.
I have an application written in Javascript which embeds Vaadin application. Since it takes a while for Vaadin to load, I need to have some way to notify js application that it has been loaded. To do that I need to pass some id of embedding app to Vaadin at startup.
The best way would be to pass it by configuration
vaadin.initApplication("embedingDiv", this.config);
but Vaadin seem to ignore any custom variables. When I try to access to init parameters with
VaadinSession.getCurrent().getConfiguration().getInitParameters());
I get something like that:
UI=com.example.tabletest.TabletestUI, resourceCacheTime=3600, productionMode=false, legacyPropertyToString=false, heartbeatInterval=300, closeIdleSessions=false, widgetset=com.vaadin.DefaultWidgetSet
which are paremeters that I set in my config file, but other parameters set there seem to be ignored.
Is it possible to pass config parameters this way? Or any other, I just need them to be accessible in init method.
I solved the problem by simply adding URI parameter to browserDetailsUrl field.
pConfig.browserDetailsUrl += "?foo=bar";
I can access it with
VaadinService.getCurrentRequest().getParameter("foo");
or
request.getParameter("foo");
in init method.
I'd like to develop a vaadin application with spring. A user should be able to login and see uer-specific pages. I he comes to the site from a different browser window, it would be handy if the user is still logged in, so preserves the session.
Now I wonder which scope to use for this purpose? Vaadin and Spring offer a #Scope("session"), #Scope("ui") and a #Scope("prototype"). Should I just place all of my *View and *Presenter classes in the session scope?
Or would it be better to have the view annoted with prototype, so that each time a pages is changed the page is recreated with updated data?
In this case, should then the presenters be still session scope, or also prototye?
Or should I at all use the ui scope that's provided by vaadin4spring boot addon? This would then create a new "uiSession" for every new tab.
#Scope("session")
class MyPresenter {
//each view should be wired to a presenter
#Autowired
private MyView view;
}
#Scope("prototype") //"session", "ui"?
class MyView extends VerticalLayout {
#PostConstruct
public void init() {
addComponent(new Label("time is: " + new Date()));
}
}
vaadin stores (large amounts of) state in the session. state is pretty much anything, that is visible to the client in the browser. the ui scope is what you should use, if any component is involved. even if ui and session seem the same, you should stick to ui to prevent raceconditions and worse against the vaadin code.
in your example above, view is definetly ui, because it is a component. this makes the presenter ui also as it holds reference to an ui scoped component and will for sure tap into events from it or read data etc.
I am wondering if it possible to do the equivalent of
public void start(BundleContext context)
{
String filter = "filter for my specific service";
context.addServiceListener(new MyServiceListener(), filter);
}
with Spring DM. I have found reference material for adding listeners for lifecycle management. But this relates to the lifecycle of the service being registered. What I need is to be made aware of the lifecycle of any service that is registered/unregistered of a specific service interface.
The bundle that needs to know this is not actually creating the service, but will be using it. Since there will multiple instances of this service that will change at runtime, I cannot simply create a service reference in my Spring configuration.
Spring DM includes support for referencing a collection of services. Unfortunately, as a new user, I can't post links, but this functionality is described in section 7.2.2 of the Spring DM 1.2 documentation. Spring DM automatically updates the contents of this collection at runtime as services are added to and removed from the service registry.
When you declare a reference upon a service, be it as a single reference, or a collection of services, you can also declare a listener bean that will be notified as services come and go. This is documented in section 7.2.3.
I'd recommend making use a List or Set of services, and using a listener bean so that you're notified of changes to the collection's contents.