osgi same class but classcastexception - osgi

Getting an issue where class A cannot be cast to class A in osgi. Point is this class A is not being exported in any other bundle as well as where it is created. In the pom for exported-package it is mentioned "!A" so A is not exported. Still we get the issue. A re-install of the bundle works though. Cant recreate the issue and then suddenly it is back again . Any ideas.
The class is an entity class and internal - not in any other project
In maven-bundle-plugin we have
<Private-Package>
com.xxx.yyy.entity.*
</Private-Package>
<Export-Package>
!com.xxx.yyy.entity.*
</Export-Package>

We had similar issues when we used technologies that had String-Class caches without direct wiring to the bundle of the Class.
Let's say there is Bundle A and Bundle T (technology). Bundle A is updated and but the classes that were loaded during the first time Bundle A was started are still in the cache of Bundle T.
This can become really magical if there is a technology in the OSGi container that updates bundles during their restart. One typical case is when a technology wants to do bytecode manipulation on classes. It implements a Bundle Weaving Hook, but it must update all of the relevant bundles that were started before the magical technology bundle to be able to catch the classloadings. An example of such magical technology is Apache Aries Proxy.
Btw.: A stacktrace and some source code would help a lot to see in which technology the issue appears.

Related

java.util.ServiceConfigurationError Provider not a subtype while using OSGi bundle

I'm creating a Liferay 7.1 OSGi bundle, which has some external dependencies in it. In consideration of time, we opted to embed the external JAR in our OSGi Bundle. I've managed to create a bnd file, which includes all of the ElasticSearch dependencies, and put them on the bundle classpath. I've used the source-code from github (https://github.com/liferay/liferay-portal/blob/master/modules/apps/portal-search-elasticsearch6/portal-search-elasticsearch6-impl/build.gradle) and the bnd.bnd file, to check what's imported.
When activating the bundle, an exception is thrown:
The activate method has thrown an exception
java.util.ServiceConfigurationError: org.elasticsearch.common.xcontent.XContentBuilderExtension: Provider org.elasticsearch.common.xcontent.XContentElasticsearchExtension not a subtype
at java.util.ServiceLoader.fail(ServiceLoader.java:239)
at java.util.ServiceLoader.access$300(ServiceLoader.java:185)
at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:376)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:404)
at java.util.ServiceLoader$1.next(ServiceLoader.java:480)
at org.elasticsearch.common.xcontent.XContentBuilder.<clinit>(XContentBuilder.java:118)
at org.elasticsearch.common.settings.Setting.arrayToParsableString(Setting.java:1257)
The XContentBuilderExtension is from the elasticsearch-x-content-6.5.0.jar,
the XContentElasticsearchExtension class, is included in the elasticsearch-6.5.0.jar. Both are Included Resources, and have been put on the classpath.
The Activate-method initializes a TransportClient in my other jar, hence it happens on activation ;).
Edit:
I've noticed that this error does NOT occur when installing this the first time, or when the portal restarts. So it only occurs when I uninstall and reinstall the bundle. (This is functionality I really prefer to have!). Maybe a stupid thought.. But could it be that there is some 'hanging thread'? That the bundle is not correctly installed, or that the TransportClient still is alive? I'm checking this out. Any hints are welcome!
Edit 2:
I'm fearing this is an incompatibility between SPI and OSGi? I've checked: The High Level Rest Client has the same issue. (But then with another Extension). I'm going to try the Low-Level Rest Client. This should work, as there are minimal dependencies, I'm guessing. I'm still very curious on why the incompatibility is there. I'm certainly no expert on OSGi, neither on SPI. (Time to learn new stuff!)
Seems like a case where OSGi uses your bundle to solve a dependency from another bundle, probably one that used your bundle to solve a package when the system started.
Looking at the symptoms: it does not occur when booting or restarts. Also it is not a subtype.
When OSGi uses that bundle to solve a dependency, it will keep a copy around, even when you remove it. When the bundle comes back a package that was previously used by another bundle may still be around and you can have the situation where a class used has two version of itself, from different classloaders, meaning they are not the same class and therefore, not a subtype.
Expose only the necessary to minimize the effects of this. Import only if needs importing. If you are using Liferay Gradle configuration to include the bundle inside, stop - it's a terrible way to include as it exposes a lot. If using the bnd file to include a resource and create an entry for the adicional classpath location, do not expose if not necessary. If you have several bundles using one as dependency, make sure about the version they use and if the exchange objects from the problematic class, if they do, than extra care is required.
PS: you can include attributes when exporting and/or importing in order to be more specific and avoid using packages from the wrong origin.
You can have 2 elastic search connections inside one Java app and Liferay is by default not exposing the connection that it holds.
A way around it is to rebuild the Liferay ES connector. It's not a big deal because you don't need to change the code only the OSGi descriptor to expose more services.
I did it in one POC project and worked fine. The tricky thing is to rebuild the Liferay jar but that was explained by Pettry by his google like search blog posts. https://community.liferay.com/blogs/-/blogs/creating-a-google-like-search (it is a series but it's kind of hard to navigate in the new Liferay blogs but Google will probably help) Either way it is all nicely documented here https://github.com/peerkar/liferay-gsearch
the only thing then what needs to be done is to add org.elasticsearch.* in the bnd.bnd file in the export section. You will then be able to work with the native elastic API.

Osgi cyclic reference needed for xstream object deserialization?

Bundle A depends on Bundle XStream.
However Bundle XStream also needs access to classes in Bundle A, otherwise I can't do object deserialization ala (BundleA.class)xstream.fromXml(xmlString)
Now what I did is to Import-Package in Bundle XStream on my BundleA packages and exported them in BundleA, but since BundleA uses XStream as well, Eclipse detects a "cyclic reference". It all seems to run fine, but I don't see the point of this Eclipse error then?
How else would I solve this in osgi?
Generally, you should try to get a clear client-server package relation among your bundles. So if possible at all, you should get rid of the cyclic reference. In your situation that is possible.
In contrast to what you indicate, your XStream bundle is able to do de-serialization of objects from another bundle if you are able to tell the functionality to use a different class loader for loading the classes indicated in the stream. Since it seems you are using XStream, you can use:
xstream.setClassLoader(bundleAClassLoader);
where bundleAClassLoader is a class loader that has access to your domain classes (the class loader of bundle A). The Import-Package for the XStream bundle is in that case not necessary.

Restlet converter registration in OSGI environment

We run Restlet 2.1 in an OSGi environment (Equinox) as bundle (ie. not as library within a bundle). The problem is that the Restlet Engine does not detect helpers (like converters) that are provided by Restlet extensions. Specifically, the EngineClassLoader#getResources() call does not return any result. The extensions are also deployed as OSGi bundles in the target platform.
Is automatic converter registration actually supposed to work within OSGi environments?
In fact, Restlet supports such feature thanks to a dedicated activator (see the Activator class in the package org.restlet.engine.internal).
This activator introspects bundles to find out the following things:
servers corresponding to registered servers
servers corresponding to registered clients
authenticators corresponding to registered clients
converters
Be aware that to use this feature, we must use the OSGi edition of Restlet since it's the only that has the MANIFEST file of the org.restlet bundle with the activator class specified. Otherwise you don't have to care about the bundle loading order...
Hope it helps you.
Thierry
Unless the Restlet-bundle explicitly imports the packages that contain the extensions (and I doubt it does, and it shouldn't), it wouldn't be able to load them, because bundles have isolated class-spaces.
A possible solution would be to provide the extensions as fragments attached to the Restlet-bundle. Thus, if you make it use the bundle-classloader (the documentation says this can be done by setting the Engines classloader), it would be able to load classes from the fragments.
Indeed it doesn't quite work for OSGi, as it depends on the ability to see the entire class space.
The way to do this in OSGi would be to use the service registry for the extensions, but that only works for OSGi aware libraries.
There is some help on the way: In the recently released OSGi 5 (Service Loader Mediator) there will be support to 'bridge' META-INF/services (I don't know if Restlet uses those, though) onto OSGi services, so 'legacy' libraries should work well within OSGi.
There is an implementation in Apache Aries called Spi-Fly. I looked at it briefly a while back. It might do the trick for you, it might not.

What is the natural start order for package-dependent OSGI bundles (under Karaf)?

I have a problem on 2.2.8 version of Karaf (and most probably on the earlier versions too).
I'm going to use Karaf to host the system with dynamically deployed bundles. Bundles are deployed by users and i cannot know beforehand which are they.
I expect order of the BundleActivator.start() to exactly correspond to package dependencies between bundles (dependencies of import/export packages) and planning to expect that it will be safe to assume that bundle0 will be completely initialized before bundle1 is going to be started. But it is not so - it seems that BundleActivator.start() is invoked in a "random" order and disregards package dependencies between bundles.
Sample use-case, I have 3 libs
test-lib0 - defines testlib0.ITestRoot, exports testlib0 package
test-lib1 - defines testlib1.TestRoot implements ITestRoot, exports testlib1 package
test-lib2 - uses both libs, ITestRoot and TestRoot
When Karaf is started, i see following sample output in console
karaf#root> TestLib1Activator.start()
TestLib2Activator.start()
ITestRoot: interface com.testorg.testlib0.ITestRoot - 16634462
TestRoot: class com.testorg.testlib1.TestRoot - 21576551
TestLib0Activator.start()
but i expect it should be always in this order
TestLib0Activator.start()
TestLib1Activator.start()
TestLib2Activator.start()
ITestRoot: interface com.testorg.testlib0.ITestRoot - 16634462
TestRoot: class com.testorg.testlib1.TestRoot - 21576551
I'm attaching sample project for tests. Test case: after "mvn install" just move jars from ./deploy folder to the same folder of Karaf, trace messages should appear in console.
(Note: it may work correctly from the first attempt, try one more time then :))
Sample test project
http://karaf.922171.n3.nabble.com/file/n4025256/KarafTest.zip
Note: this is cross-post from http://karaf.922171.n3.nabble.com/What-is-the-natural-start-order-for-dependent-bundle-td4025256.html
In OSGi the bundle lifecycle is installed → resolved → starting → started.
Import-Package and Export-Package only influence when the bundle goes from installed to resolved. So the framework makes sure all bundles you import packages from are resolved before your bundle but then your bundle only goes to the resolved state. Then in a second step the activators are called. So you can not assume the activators are called in the same order. If you need some initializations before your testlib2 can work then you should use OSGi services.
So If I understood your case correctly then you testlib0 defines an interface, testlib1 implements it and testlib2 wants to use the implementation. So the best way to achieve this is to publish the impl as an OSGi service in testlib1 and reference this service in testlib3. You can then use the service with a ServiceTracker or with e.g. blueprint. I have a small example that shows this: http://www.liquid-reality.de/x/DIBZ . So if you do your case like in my example blueprint makes sure that the context of testlib2 only gets started when the service is there. It will even stop testlib2 when the service goes away.

how to use a service in the class file generated using javaassist

Can i use a service in the class file which is generated using javaassist. how can i achieve that?? and as this class is created dynamically how can i register that this class is using the provided service of another class?
I've asked a similar question here.
The smallest unit of 'installation' would be a bundle. You could generate a sort of 'virtual bundle' around your class file, including a OSGI-INF/componentX.xml descriptor, and install that into OSGi.
If it changes, regenerate the bundle and update OSGi.
I didn't go down this path as I have a lot of generated code (about 4500 different scripts that all have dependencies) and I fear that I'd get into problems if I'd generate that many bundles.
I also got a tip about the Felix Dependency manager, but I haven't thoroughly checked that out yet, it might do the things we need.

Resources