What are the best practices to customize a OSGi bundle shipped to a customer ?
The bundle has a service with 50 APIs, the customer would like to extend or inherit a single API. Does the customer have to create a new bundle or can he modify the existing bundle ?
I would like that the customer does not have to modify the base bundles, is their a way to simply replace the base bundle with a customized bundle ? Also how will process be automated, each time I ship a base bundle, the customer will have extract the bundle contents, repackage this base contents with customized version of classes, with appropriate spring configuration and the rebundle it with a different name or version number. Then replace the base bundle with this custom version in the OSGi container. I am assuming the base bundle only Exports interfaces and not implementation classes, so nobody can inherit implementation classes directly.
Your bundle has a public contract in the packages it exports. Your customer can write a bundle which imports your package and uses them. The customer could also export a package (of his own) which can include types which extends (or delegate) to your types. This exported package is then the public contract of the customer for use by his customers (including company internal customers).
I don't think you want to be in the place where your customer modifies your bundle to customize it.
Related
I have two bundle,X and Y.Both of them were installed and active as bundle on OSGi.Function sayHello in Bundle X and function writeHello in Bundle Y.I need to call sayHello function from Bundle Y and I will write returned string with writeHello function in Bundle Y.
Can you say that it is possible or not?If it is possible how can deploy this logic?
In my mind,I have something for example;
I will add dependency of Bundle X in pom of Bundle Y,but how can I see function sayHello after adding dependency?
Thank you..
There are two ways to access a functionality of another bundle. For both ways you need an Export-Package in the bundle offering the class and an Import-Package for the package of the external classes you want to use.
Create an instance of the class and use it
Use this approach if the class you want to use is simple to setup and you do not need decoupling. This is a typical case for simple libraries.
Publish the functionality as an OSGi service and bind the service
Use this approach if it is difficult to instantiate the class and you do not want the client to be involved with this. The service approach also makes sense if you want to decouple from the implementation. To achieve this you create an interface for the functionality and publish the service with the interface. The client can then bind the service by specifying only the interface.
As it is mentioned we can install multiple bundles of different versions in OSGI. SO if I have two versions of my Bundle and both are active . How will my jsp recognize which bundle has to refer ?
In short, you can't do this from a JSP. In AEM/Sling, all JSPs (in fact, all resource scripts) share a single classloader and there's no way to specify a package import version. As a result, if you have two bundles exporting the same package with different versions (which is generally what would happen in the scenario you described), you can't know which version will be used in your JSPs. I believe currently it is whatever is exported by the bundle with the lowest ID, but this is not at all guaranteed and shouldn't be depended upon.
You should try to avoid this scenario by reducing your JSPs hard dependency on Java implementation classes and instead use OSGi services for whatever it is you are trying to accomplish. With services, it is relatively easy to select a particular service implementation by the use of a filter.
For example, if multiple bundles have services registered with the com.myco.Foo interface (and this interface is exported by a common bundle) and each service registration has some unique property set (in my example below, this property is named type, but that's arbitrary), you can do something like this:
<%
Foo[] foos = sling.getService(Foo.class, "(type=something)");
if (foos != null && foos.length == 1) {
Foo foo = foos[0];
// do something with foo
}
%>
For every Import-Package package declaration, there must be a corresponding Export-Package with the same package
Bundles can also attach other attributes to the packages it imports or exports.
For ex:-
Bundle-Name: Bundle A
Import-Package: org.apache.foo;version="1.2.0"
This means, Bundle-A has a dependency on package org.apache.foo with a minimum version of 1.2.0.
Although with OSGI you can specify a range of versions, if you don't specify a range but rather use a fixed version, it will result in a meaning of "a minimum" of the fixed value.
If there is a higher version for that same package, the higher version will be used.
So Bundle-A will not resolve correctly unless there is a corresponding bundle B that exports the required package
Bundle-Name: Bundle B
Export-Package: org.apache.foo;version="1.2.0"
Note that the reverse is not true.If Bundle B exported version 1.2.0, Bundle A is not required to specify a version 1.2.0.
This is because imports declare the versions they need. An exported version does not specify anything.
You can check the existing versions of your bundles in the depfinder-"http://localhost:****/system/console/depfinder"
In simple we can say JSP will recognise the bundles through version only.
If there are two versions with similar name,it will recognise the latest version.
Bundles installed first are used to satisfy a dependency when multiple packages with the same version are found.
My application obtains class names from a properties file. Classes represented by these class names could reside in certain OSGI bundles unknown at advance so in order to instantiate them I firstly have to find which bundle these classes belong to. I'm thinking about getting all installed bundles from BundleContext#getBundles, this means I have to obtain reference to BundleContext in AbstractUIPlugin#start. But I'm not sure if holding reference to BundleContext is the right thing to do since it's should be used only in the start method. So I need advice from OSGI experts here about the alternatives to get list of bundles.
Any help would be greatly appreciated.
Regards,
Setya
This is not really how OSGi is intended. If a bundle has something to add to the 'global' context, you should register a service. So each bundle that has something to share can do that in its own start method.
Some other component (DS, ServiceTracker, Blueprint, something like that) can then listen to these events, and act accordingly.
This is really important, if you start manually searching through all bundles you completely lose the advantages of OSGi.
Like written before you should try to use services to achieve what you want. I guess you have an Interface with one or more implementations that should be installable at runtime. So if you control the bundles that implement the interface then simply let them install their implementation as a service by using an Activator or a blueprint context. You can use service properties to describe your implementation.
The bundles that need the implementation can then lookup the services using a service tracker or a service reference in blueprint.
If that is not possible then you can use the bundle context to obtain the running bundles and instantiate the classes but this is not how OSGi should work. You will run into classloading problems as the bundle that tries to instantiate the classes will not have direct access to them.
Your bundle gets control at start up through the bundle activator, or better, through DS. At that time it can register services with the services registry so others can find/use them.
The route your planning to go (properties that name classes) is evil since you undoubtedly will run in class loading hell. Modularity is about hiding your implementation details, the name of your implementation classes are such details.
Exposing implementation classes in properties files is really bad practice and it looses the advantage of modularity. It does not matter if another class refers to your implementation class or a property file, the problem is that the impl. class is exposed.
Unfortunately this model has become so prevalent in our industry that many developers think it is normal :-(
OSGi allows you share instances typed by interfaces in a way that allows the implementation class to only be known inside the module.
Do the packages that contain OSGI services need to be exported in order to publish to the service registry? Or does publishing to the service registry get round the need to export the package?
Short answers; 1st question = no, 2nd question = no, there's no "workaround"
The principle of "Design by contract" comes into play. Basically we provide an interface for consumers to depend on and providers to implement - this decouples our consumer code for the implementation details of our implementors. See http://www.osgi.org/blog/2011/08/package-as-contract.html for a good overview and http://en.wikipedia.org/wiki/Design_by_contract.
Given the interface (the API we wish to use) and the implementation (the concrete implementation of the API interface) and the consumer (code wishing to use an implementation of the interface), with OSGi you have a number of options.
A bundle can provide both interface and implementation, but it must export the interface if this service is to be used outside of the bundle itself
Having a separate bundle for the interface (which it exports) and other bundle(s) for the implementation(s) - the implementing bundles must import the interface's package but need not reveal their own implementation.
The advantages of number 2 include allowing multiple providers (OSGi uses LDAP filters that can be used to specify metadata matching on the registered services' properties), and leveraging the dynamic binding nature of OSGi (i.e. you can change implementation provider at runtime).
One thing to bear in mind, is that the consumer and provider must use the same classes as defined by the package version and exporting bundle (in other words the bundle classloader that loads the API classes must be the same for both consumer and provider).
The types of the service should be in exported packages. Services are typed and build upon the type sharing of the module layer. So you need to make sure that the provider of the service and the consumers of the service can all share the same type. So some bundle must export the package containing the type so that the provider and the consumers can import the package. This allows the framework to verify type safety of the service.
I have a class with dependencies which I want to hot deploy without restarting the dependencies. The class has an interface but there's only one concrete implementation.
Initially I created a single bundle with exported the interface and registered the implementation using activator and implementation classes which were not exported. However, if I update the bundle, bundles which use the registered service get restarted after the update when PackageAdmin#refreshPackages is called (this is automatic when using fileinstall).
I have fixed this by creating a separate api bundle.
Is this the best way to achieve this?
Would you ever have a bundle which exports its api and includes the implementation in the same bundle. As far as I can see any give bundle would either export all its classes or no classes. What am I missing?
It is definitely a best practice to separate API bundles from their implementations in OSGi. If you do this, then any bundle that uses the API only needs to import classes from the API bundle, which can allow you to change implementations at runtime without restarting your dependent bundles.
Ideally your implementation bundle would implement the interface and export implementation as a service on the API provided interface. This allows the client bundles to utilize the service without referencing the implementation bundle.
In Apache Sling we do both: major APIs are in their own bundles, but for smaller things like extensions or optional components we often provide the default implementation in the same bundle as the API.
In the latter case, you can still allow for those default services to be replaceable, for example using service ranking values when you want to override them.
A bundle does not have to export all its classes, our bundles which include default services export just the API packages (and the default implementations are in different packages, obviously).
Unless there is a hard requirement to be able to replace implementation at runtime, without restarting client bundles, I would personally advocate keeping the explicit dependency link between API and implementation (either by including impl classes in the API bundle, or by having the API bundle import implementation packages from the impl. bundle).
The pb with the patterns suggested above is that they break the dependency chain. The benefits of dependency management go far beyond simple API compatibility, they also include ensuring predictable, consistent runtime behavior, as well as compatibility with the deployment ecosystem, and all of those require managing the implementation dependencies.