Apache Felix: inherit import package org.osgi.service.cm - osgi

I have one bundle which has Import-Package org.osgi.service.cm. In this bundle there is only an interface ConfigurationInterface, that declares a couple of methods, one throws an org.osgi.service.cm.ConfigurationException. This bundle exports only its own package, lets say com.foo.bar.configuration.
Then I have other API bundles that have an interface for the service, ServiceInterface, that extends ConfigurationInterface, so they are importing the package com.foo.bar.configuration. Obviously there are also implementations bundle for these api that implement ServiceInterface, so they are importing org.osgi.service.cm because every implementations need to have the method that throws an org.osgi.service.cm.ConfigurationException.
Everything is working fine, the problems come out when I declare these services as optional, because when they are not available the framework tries to instantiate a proxy from the interface and I get a java.lang.ClassNotFoundException: org.osgi.service.cm.ConfigurationException. The framework suggests to add an import for 'org.osgi.service.cm' to the API bundle.
Is there a way to make this import available from the configuration bundle so that it is not necessary to add the import to every API?

Related

Micronaut declarative REST client throws an error - #Introduction method interceptor missing

When I autowire the client interface for my Micronaut declarative client, I get this error:
Caused by: java.lang.IllegalStateException: At least one #Introduction method interceptor required, but missing. Check if your #Introduction stereotype annotation is marked with #Retention(RUNTIME) and #Type(..) with the interceptor type. Otherwise do not load #Introduction beans if their interceptor definitions are missing!
at io.micronaut.aop.chain.InterceptorChain.resolveIntroductionInterceptors(InterceptorChain.java:194)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1494)
What's the proper way to fix it?
Details
I have an established Grails application that I recently upgraded from 3.x to 4.0.1.
This app has a service which does several REST calls in parallel, and I am trying to add a new REST call that uses the new Micronaut HTTP declarative client.
I added the client library to dependencies in build.gradle:
compile "io.micronaut:micronaut-http-client"
My client interface looks like this (in src/main/groovy):
package com.mycompany.xyz.rest
import com.mycompany.xyz.rest.myendpoint.Results
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.client.annotation.Client
#Client('xyzRest')
#Header(name = 'myauthkey', value = '${myAuthKey}')
interface XyzRestClient {
#Get('/myendpoint')
Results myendpoint(String param1, String param2)
}
package com.mycompany.xyz.rest.myendpoint
import com.mycompany.xyz.rest.myendpoint.DataItem
import groovy.transform.CompileStatic
#CompileStatic
interface Results extends List<DataItem> {
}
I configured the URL in application.yml:
environments:
development:
micronaut:
http:
services:
xyzRest:
urls:
- http://xyz.mycompany.com/rest/v1
The message about #Introduction makes me think that Micronaut is not doing the process of compiling the declarative client. Is there some
What else am I missing?
Update:
I tried changing the build.gradle dependency to implementation as shown in the Micronaut docs, insteadl of compile, as shown in the Grails docs. No dice.
Update 2:
I found that the constructor for HttpClientIntroductionAdvice is never invoked during startup. I don't know why it's not being included in my project. IntelliJ shows micronaut-http-client:1.1.4 in external libraries, and it's set to compile scope.
A gradlew clean seems to have fixed the issue.
I tried to work backwards and duplicate the problem for posterity's sake, but so far I have not been able to.

Force Imports in Apache Karaf

I have bundle that deals mostly with interfaces and it uses a factory from a thrid party jar to get the instances for the interfaces it is using.
For example,
my-bundle.jar has...
com.oth.itf.Intrface itf = Fctry.getInstance('ABC');
has the
imports-package for com.oth.itf
third-party.jar has..
public static com.oth.itf.Intrface getInstance(String abc) {
if (...) {
return new com.oth.impl.ItfInstance();
}
}
has the exports-package for com.oth.itf and com.oth.impl
Everything works fine and bundles also get deployed but the issue is that I am getting
ClassNotFoundException for ItfInstance
on
my-bundle
when the code gets executed.
I tried adding import-package for com.oth.impl but to no avail. Things started to work when I added a
dummy declaration of com.oth.impl.ItfInstance
some where in my-bundle.jar. Looks like, Karaf gets the imports only if we explicitly use them. Is there a better way? Is there a way to force Karaf to import packages even if we don't use them explicitly?
Neil is right, if it's not used in the code it can't be imported. With Karaf you have the possibility to help. With the command bundle:dynamic-import you can add a dynamical import to the bundle on run-time. With this you're able to find the actually needed imports via bundle:headers you'll find the imported packages of this bundle. Take those and add those missing imports to your manifest generation and your're set.
If you doesn't has bundle: option, uses this dev: command:
dev:dynamic-import <BundleID>
This allows Karaf import dependencies on runtime.

Behaviour of equinox service registry with service implementing two interfaces

I'm having a problem with service resolution of the equinox service registry implementation and I'm not sure whether this is a bug.
Here is a short description of my bundles and runtime:
Bundle com.foo.api
exports com.foo.base with an interface IFooService
IFooService is not inherited from ManagedService or in any form related to it
Bundle com.foo.impl
imports com.foo.base
imports org.osgi.service.cm
registers FooServiceImpl which implements IFooService and also ManagedService from org.osgi.service.cm
This it the component.xml of FooServiceImpl:
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
immediate="true" name="com.foo.impl.FooServiceImpl">
<implementation class="com.foo.impl.FooServiceImpl"/>
<service>
<provide interface="com.foo.api.IFooService"/&gt
<provide interface="org.osgi.service.cm.ManagedService"/&gt
&lt/service>
<property name="service.pid" value="fooservice"/>
</scr:component>
Bundle com.foo.user
imports com.foo.base
imports org.osgi.service.cm
implements a component needing a IFooService (it does only request this interface and not the ManagedService interface)
The component.xml
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
name="com.foo.user.BarComponentImpl"
enabled="true" immediate="true">
<implementation class="com.foo.user.BarComponentImpl" />
<reference interface="com.foo.api.IFooService"
name="fooService" policy="static" cardinality="1..1"
bind="bindFooService" unbind="unbindFooService"/>
</scr:component>
Runtime
I'm using equinox, in config.ini the bundles described above, org.eclipse.osgi.service and org.apache.felix.configadmin are loaded.
(Beside others of course, but they are not of interest right now)
org.eclipse.osgi.service and org.apache.felix.configadmin both provide the package org.osgi.service.cm
org.eclipse.osgi.servce provides version 1.4
org.apache.felix.configadmin provider version 1.5
The problem
Depending on the order of the bundles in config.ini it can happen that com.foo.user does not get a refence to FooServiceImpl.
Debugging into the equinox runtime I found that this happens:
com.foo.impl uses org.osgi.service.com version 1.4 (from the org.eclipse.osgi.service bundle)
com.foo.user uses org.osgi.service.com version 1.5 (from the org.apache.felix.configadmin bundle)
The equinox registry detects that the interface IFooService is known by com.foo.user but the interface ManagedService which is also implemented by IFooServiceImpl is not compatible between com.foo.impl and com.foo.user. The registry therefor does not return a service reference. Although the declarative services implementation lists the dependency as resolved using the comp command at the osgi console.
Is this the desired behaviour in this case? Shouldn't the framework return a reference to FooServiceImpl when requesting a IFooService even if the service implements another interface that is not compatible with the using bundle?
I didn't find any statement in the OSGi specification, but before opening a bug at eclipse I wanted to hear what the experts think.
Update
Thanks to BJ Hargrave the main problem is solved (see below), but I'm still wondering why the framework handles these two cases differently
Bundle A requests IFooService, it imports only com.foo.api
Bundle B requests IFooService, it imports com.foo.api and org.osgi.service.cm (but in a "wrong version")
Bundle A gets a reference to the service, Bundle B doesn't.
Whats the difference between
"an interface is not known by a bundle" and
"an interface is imported in an incompatbile version"
when this interface isn't acutally used when requesting service?
Shouldn't the framework return a reference to FooServiceImpl when requesting a IFooService even if the service implements another interface that is not compatible with the using bundle?
It is specified only to return services which are type compatible with the requester. You don't provide the specifics of how you register and find the service in question to provide a more definitive answer.
The solution here is to not install the org.eclipse.osgi.service bundle. A bundle such as that (a collection of unrelated, exported packages), causes these sorts of problems. org.eclipse.osgi.service is useful at compile time since you have access to a large palette of services. But at runtime, it creates the sort of problems you see. At runtime, it is better to have either the service implementation export the package, as Felix CM does, or have a bundle export just the service package to support the implementation and its clients.
OSGi provides a osgi.cmpn.jar, which is like org.eclipse.osgi.service, for use at compile-time. The latest versions of this jar include an unresolvable requirement to prevent people from using the jar as a bundle at runtime.
I agree with BJ that it is probably the normal behaviour in OSGi. What you could try to solve the problem is to register the service separately for each interface. If that still does not help you could create a spearate class implementing ManagedService that forwards the config updates to the main service.

Why imported classes take precedence over bundle classes?

I know that the specification exactly defines it but cannot get what is the reason for this:
A class space is then all classes reachable from a given bundle’s class loader.
Thus, a class space for a given bundle can contain classes from:
• The parent class loader (normally java.* packages from the boot class path)
• Imported packages
• Required bundles
• The bundle's class path (private packages)
• Attached fragments
Let's assume:
A bundle declares "import-package: a"
There is a local class a.X in this bundle
There is a class a.X in another bundle
new a.X() would load the class from another bundle.
What is the reason that imported classes take precedence over bundle classes? Is it just a consequent continuation of java hierarchical class loading policy?
This is actually a core aspect of OSGi.
Sharing classes
The whole import/export mechanism is intended to let different bundles use the same class when communicating. Same in this case means not only binary equal, but loaded by the same class loader (recall that every bundle has its own class loader). If the bundle's own classes would be favored over imported ones, bundles would not be able to 'agree' on which copy of a class to use.
But... why?
Why would you have a copy of a class, which you also intend to import?
Consider a situation in which you want to do some logging, so you import org.osgi.service.log, but it's not a vital aspect, you can happily run without a LogService present. Now,
if you would only import the package, your bundle would fail to resolve, and thus fail to start, and
if you would only include the class, you would never use the other bundle's LogService class, so you cannot use the service (this is the agreeing part).
In this situation, you both import and include the class, so you can run in either situation, using your own copy when no one else has one, and sharing a copy if someone else does. You can even choose to export your copy, and let the framework decide.
As a sidenote, this is exactly the reason you should (almost) always import what you export.

How can I use BundleWiring to lookup Bundle/Class relationships (previously done via PackageAdmin)?

I'm in the process of upgrading my application the latest release of Eclipse Equinox 3.7 and associated libraries. I'm excited to have the generic support in OSGi 4.3 and digging the new Bundle.adapt() concepts. I've run into one stumbling block.
Previously, I've used PackageAdmin.getBundle(Class) to find out which bundle loaded which classes. I have to do this due to some RMI object serialization usage.
QUESTION: Is there an way to accomplish the same result, mapping Class to Bundle, using the new BundleWiring API?
I realize that PackageAdmin is probably not going away anytime soon, but I hate deprecation warnings.
Kinda embarrassed that I didn't find this the first time I looked through the document. Answering my own question for completeness.
From the core specification PDF ...
Section 3.9.9 - Finding an Object’s Bundle
There are scenarios where a bundle is required in code that has no access to a Bundle Context. For this
reason, the framework provides the following methods:
Framework Util – Through the FrameworkUtil class with the getBundle(Class) method. The
framework provides this method to allow code to find the bundle of an object without having the
permission to get the class loader. The method returns null when the class does not originate from
a bundle.
Class Loader – An OSGi framework must ensure that the class loader of a class that comes from a
bundle implements the BundleReference interface. This allows legacy code to find an object’s
bundle by getting its class loader and casting it to a BundleReference object, which provides
access to the Bundle. However, this requires the code to have the permission to access the class
loader. The following code fragment shows how to obtain a Bundle object from any object.
ClassLoader cl = target.getClassLoader();
if ( cl instanceof BundleReference ) {
BundleReference ref = (BundleReference) cl;
Bundle b = ref.getBundle();
...
}
In an OSGi system, not all objects belong to the framework. It is therefore possible to get hold of a
class loader that does not implement the BundleReference interface, for example the boot class path
loader.

Resources