I'm attempting to use Declarative Services to create a service bundle that provides functionality to another bundle. However, I want my Service Provider bundle to not start until it is needed. Let me describe my conditions.
There are two bundles:
-com.example.serviceprovider
-com.example.serviceconsumer
The Service Provider bundle provides a services using Declarative Services as follows:
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" enabled="true" immediate="true" name="samplerunnable1">
<implementation class="com.example.serviceprovider.SampleRunnable"/>
<service>
<provide interface="java.lang.Runnable"/>
</service>
The Service Consumer references the provided services as follows:
<reference name="SampleRunnable"
interface="java.lang.Runnable"
bind="setRunnable"
unbind="unsetRunnable"
cardinality="1..n"
policy="dynamic"/>
When both of these bundles are "ACTIVE" on start up, the Service Consumer has no trouble communicating with the service declared by the Service Provider. The problem happens when I try and have the service provider start in a lazy fashion.
After the Service Provider is set to load lazy this is what I get in the OSGi console:
osgi> ss
"Framework is launched."
id State Bundle
15 STARTING com.example.serviceconsumer_1.0.0.X
16 RESOLVED com.example.serviceprovider_1.0.0.X
What I would expect to see, is that even though bundle 16 is only "RESOLVED" that it would have at least registered is service. But when I call the "bundle" command, it states "No registered services."
osgi> bundle 16
com.example.serviceprovider_1.0.0.X [17]
Id=17, Status=RESOLVED Data Root=C:\apache\apache-tomcat-.0.40\work\Catalina\localhost\examplesX\eclipse\configuration\org.eclipse.osgi\bundles\17\data
"No registered services."
No services in use.
No exported packages
Imported packages
org.osgi.framework; version="1.7.0"<org.eclipse.osgi_3.8.0.v20120529-1548 [0]>
No fragment bundles
Named class space
com.example.serivceprovider; bundle-version="1.0.0.X"[provided]
No required bundles
Maybe I've missed the fundamental concept of lazy loaded bundles and services registration. If a bundle is in a "RESOLVED" state, shouldn't it have all it's "wires" connected? (ie, has a classloader, resolved import and export dependencies and services registered.) If the Service Consumer tries to access the service shouldn't that bundle transition to the "ACTIVE" state? What piece am I missing here?
Bundles in the RESOLVED state cannot provide services, and they will be ignored by Declarative Services. You should in general start all bundles during launch time, even if you want lazy loading behaviour. The key is to make the activation of the bundles cheap (or free!), and only pay for initialization of components when they are required.
DS takes care of lazy activation by default already. There is nothing you need to enable or change for this to happen. Essentially DS publishes the service entry in the registry, but it does not actually instantiate your component (or even load its class) until some client tries to use the service.
Furthermore, because DS does not load the class until required, OSGi does not even need to create a ClassLoader for the bundle, so long as your bundle does not have a BundleActivator.
To reiterate, you should not seek to make your bundles stay in RESOLVED state. Such bundles can only export static code and resources, but they cannot "do" anything and they cannot participate in the service registry.
Declarative services were designed for this case. Starting a bundle means that's functionality should be available, it does not mean it actually uses resources. Only stop bundles when you don't want is function.
This question is a good example of trying to control too much. In a component oriented world programmers should use lazy initialisation as much as possible but they should never attempt to control the life cycle.
Related
I have a persistence bundle (has a Meta-Persistence), it also has an Activator class
when deployed into karaf, the container starts processing the persistence unit &mapping files etc, before calling the start method of the Activator
My question is : is there a way to alter this behavior ? having the container call the method first ?
Edit
The reason I need the activator to be called first:
I have some mapped entities that rely on jackson which itself relies on spring, what happens is, when hibernate is loading the entities, it arrives at a home-made class that blocks while no spring context is injected into it. So the bundle is forever on a starting state
So I though a possible solution would be to make use of the Activator to inject the spring context
Thank you
The container (or more precisely Aries JPA) must start processing the persistence unit before the bundle is started.
The reason is that Aries JPA needs to do load time weaving in some cases. This kind of weaving must be done before classes to be weaved are loaded the first time by any bundle. So the only safe time to do this is directly after the bundle is resolved.
UPDATED:
I will like to know if is possible to force all the dependencies which have reference to other OSGi Service to unbind to it, for then rebind.
e.g.
I have A, B, C all OSGi services which have a service dependency to X. Then X change something and wants that all other services which are binded to it execute unbind and then bind. This without X being stopped or reloaded or change the code of the other services.
Is this possible with standard OSGi?
You seem to be confusing service dependencies and package (type) dependencies.
For service dependencies, when a service is unregistered and a replacement service registered, there are service events which are published. Clients of the service can react which is what Declarative Services, Blueprint and ServiceTracker all help with.
For package dependencies, if a bundle which exports a package is updated, then all dependent bundles which import the package are left wired to the old version of the package. These dependent bundles can be refreshed which will caused them to be stopped, get new class loaders wired to the updated package, and started again. See https://osgi.org/javadoc/r5/core/org/osgi/framework/wiring/FrameworkWiring.html#refreshBundles%28java.util.Collection,%20org.osgi.framework.FrameworkListener...%29.
I have one java application which uses OSGI model :
I have two preexisting bundles :
com.mos
com.login
com.login has a implementation and registration of Authenticator service (own service for authentication).com.login as well as com.mos uses this authentication service.
Now I am writing one new bundle (com.new) and I have to add / modify Authenticator Service so I have written my own implementation of it.
Once I start my program / application, if my new bundle com.new runs after com.login then all bundles uses new Authenticator Service.But If com.new bundles runs before com.login then old Authenticator Service is available.
Is there any mechanism in OSGI where we give some priority something like which bundle should start first.
The OSGi bundle startlevels allow you to influence the start order for each bundle.
See the according javadocs http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/startlevel/package-summary.html
But I would not recommend to do that.
Start levels should usually not be used as a way to control service startup. In OSGi service start orders are not guaranteed and services may come and go at will.
Making your new bundle (com.new) depending on the specific implementation of your Authenticator service would do the trick and guarante the correct order.
In general, when you have multiple OSGi services available, you have two options to pick one:
Service Filter
Service Ranking
Service Filter can be used to filter out services based on service properties as described here or here.
Service Ranking published by the service makes them eligible to be picked up based on the service ranking. The one with highest service ranking will be picked up as described here or here.
According to the documentation of the BundleContext.getServiceReference() method:
If multiple such services exist, the service with the highest priority is selected. This priority is defined as the service reference with the highest ranking (as specified in its Constants.SERVICE_RANKING property) is returned.
If there is a tie in ranking, the service with the lowest service ID (as specified in its Constants.SERVICE_ID property); that is, the service that was registered first is returned.
I have rather big set of services registered with registerService. For simplicity let's assume they are lookup by some property name. So pair of invocation is straightforward (I use pseudocode for property spec):
context.registerService(
IMyService.getClass().getName(), myServiceInst, {"name"="a"})
After that on client side:
context.getServiceReferences(IMyService.getClass().getName(), {"name"="a"})
For some reason I cannot register all possible combinations of name. Is it possible to intercept all OSGi queries so I could create services on the fly when they are queried?
I would like have basic solution that works on all layers of OSGi - it mean that code above and code with (for example) Declarative Service will work the same way.
Take a look at Service Hooks in the core specification. They allow you to find out who is waiting for what services. Notice that this might imply parsing the filter if you're interested in what properties they're waiting for.
I think you have a couple of options:
Option 1:
If you need only one Service object by client bundle (where the client bundle identifies the key-value pairs) consider using http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/ServiceFactory.html. I think the javadoc is pretty self explaining and you can find easily usage samples in google. In this case you have to implement ServiceFactory and you have to use that one in Declarative Services (please correct me if I have not used declarative services only blueprint)
Option 2:
Create your services with the help of ConfigAdmin. You create a configuration with your client bundle and your service provider bundle will catch that and export the necessary service. After the service is provided you can catch the new service registration with the client. You can find nice doc at http://felix.apache.org/site/apache-felix-config-admin.html. Well in case of this option you will be able to get more services by client bundles but I do not think you can use this with Declarative Services (You must catch the configuration changes programmatically).
Option 3:
Instead of registering IMyService register IMyServiceFactory as an OSGi service. that has a createService(name) function. In this case in the client bundles you have to take care of the lifecycles of your IMyService objects (if no more IMyService is used you can "unget" IMyServiceFactory).
I have already written an eclipse plugin that registers a BundleListener to the BundleContext in which it is started. The registered listener will log all BundleEvents. I noticed that there are only events of type STARTED logged.
I have registered the bundle to start at level 2 in the config.ini of eclipse with a default bundle start level of 4.
Is there any way that I am able to listen to events of type RESOLVED for example? Am I starting the bundle too late for receiving events of that type? Is there maybe more than one bundle context used?
Forcing ordering is in general a bad idea and worse in OSGi due to the dynamics. You can get something that looks like it has ordering with start levels but it is guaranteed to be very fragile and quickly becoming horribly complex. Since in general you need events to discover state I wonder what your use case is. In OSGi, finding the Bundle state is trivial. For applications that need the event for state discovery the BundleTracker provides a very nice API to discover bundles in a set of states. In my experience, the BundleTracker fits well for 99% of the BundleListener use cases.
If you're just logging, all Framework events are logged to the Log Service and the Log Service is usually started up very early. With a Log Reader Service you can get access to events that happened before you got started.