Can't get a working reference to an OSGi service - java

I'm trying to embed apache felix into a simple hello world java project with maven, but I can't find a way to get a reference to a service of a bundle. I've installed org.apache.felix.bundlerepository bundle into OSGi from a jar and also added it as a maven dependency to my project. After that I'm starting the bundle, getting BundleContext from it and then calling getServiceReference(RepositoryAdmin.class.getName()) on that bundle context. The first thing I'm unhappy with is that I have to use BundleContext from the installed bundle, if I'd use BundleContext of the Framework the ServiceReference will be always null. This is not convinient.
The second, more important issue, is that when I finally receiving a reference to RepositoryAdmin service from bundlerepository bundle I can't cast it to org.apache.felix.bundlerepository.RepositoryAdmin, executing the following code:
(RepositoryAdmin)admin.getBundleContext().getService(ref)
will throw this exception:
java.lang.ClassCastException: org.apache.felix.bundlerepository.impl.RepositoryAdminImpl cannot be cast to org.apache.felix.bundlerepository.RepositoryAdmin
I know this is a kind of classpath issue and may be caused by incompatibility of interfaces, but I'm using a bundle jar of the same version (2.0.2) as a maven dependency of my project.
I'm also aware of Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA osgi configuration option which should force a bundle to use a package from the host application, but it didnt help me.
Here is the main class of my project https://github.com/ArtemZ/osgi-study/blob/master/src/main/java/com/artemz/demo/Main.java which is messy because I was trying different things on it in order to get a reference to a service, but none actually worked for me.
Hope someone will help me, because I'm really desperate with this issue.

Thanks for giving some more details about what you are doing. I already followed the mails on the felix list.
Now I think I understand what happens. The reason why you can not get the service from outside the RepositoryAdmin bundle is that the package you define in
FRAMEWORK_SYSTEMPACKAGES_EXTRA is "org.apache.felix.bundlerepository; version=2.0.2"
is not the same version as the package from the RepositoryAdmin bundle. I downloaded the bundle and looked into the Manifest:
Export-Package: org.osgi.service.repository;version="1.0";uses:="org.osg
i.resource",org.apache.felix.bundlerepository;version="2.1";uses:="org.
osgi.framework"
So as you see the version you should export from the system bundle is 2.1 not 2.0.2.
In OSGi the versions are defined per package not on the bundle level. So while most times they are the same this is not always true. Espcecially for OSGi spec packages.
So when the package version are different you have two effects:
1. You will not be able to find a service with a different package
2. If you get a service object in some other way like you did then you will have a class cast exception as they are loaded by different classloaders.
So can you try the 2.1 version and report if it works?

Related

BundleException while trying to start an osgi bundle

I'm trying to install & start a bundle from a osgi jar in the filesystem
Bundle bundle = context.installBundle("reference:file:" + fullPath);
bundle.start();
it worked for another simple bundle, but another more complex bundle has
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
in its manifest
this causes the following exception
(org.osgi.framework.BundleException) org.osgi.framework.BundleException: Unresolved constraint in bundle
Unable to resolve 42.0: missing requirement [42.0] osgi.ee; (&(osgi.ee=JavaSE)(version=1.8))
how do I add this capability to my project?
also how would I remove this require from the other project?
all similiar questions I found didn't answer my question
thanks in advance for any answers and for helping me not pull out my hair :)
Edit:
as christian suggested I tried finding the configuration to felix in netbeans, as the felix framework is loaded by netbeans. I found some configurations inside the maven POM file, but could not use the "org.osgi.framework.system.capabilities" framework property which was mentioned by christian, which I couldn't find in the documentation.
I am putting a bounty as this is really important for me to resolve and is the only thing preventing me from using OSGi as far as I can see
The capability you identified:
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
is a requirement for the osgi.ee namespace. This is the namespace which defines the execution environment for your framework. The filter then says that you need the execution environment to be JavaSE, and that you need the version attribute to be 1.8. This (unsurprisingly) corresponds to Java SE 8.
As others have indicated, this capability should be provided by the OSGi framework. You should not need to do anything to get this to happen. There are two main reasons that this capability would not be present.
You are using an older, or non SE, version of Java. This could easily happen if your NetBeans launch configuration is not using a Java 8 installation.
You are using a Felix framework which does not understand "new" versions of Java. This could be because your Felix framework is old (the latest version is 5.6.4) or because your Java version is very new (are you using a pre-release Java 9 build?).
This really should just work if you can run a newish Felix framework on top of Java 8. Do you have any more details about your environment?
Edit:
You can see the osgi.ee capability provided by the system bundle as follows:
// Get the wiring for the system bundle
BundleWiring wiring = context.getBundle(0).adapt(BundleWiring.class);
// Get the osgi.ee capability for the system bundle
List<Capability> eecaps = wiring.getCapabilities("osgi.ee");
// There must be exactly one capability to show
System.out.println(eecaps.get(0).getAttributes());
This is a capability that needs to be provided by your framework. It means that your project needs to run on Java 8.
It is configured in the framework property:
org.osgi.framework.system.capabilities=osgi.ee; osgi.ee="JavaSE"; version:List<Version>="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8"
That said normally this is already configured when you for example start the felix distro.
This is a known felix bug which has been fixed in 2.0.4. From http://svn.apache.org/repos/asf/felix/releases/org.apache.felix.bundlerepository-2.0.10/doc/changelog.txt ,
Changes from 2.0.2 to 2.0.4
---------------------------
** Bug
* [FELIX-3097] - LocalRepository is not updated when bundles are
* [FELIX-4571] - NullPointerException when using Repository impl with Aries subsystem impl
* [FELIX-4616] - BundleRepository ResourceComparator violates comparison contract
* [FELIX-4640] - missing (&(osgi.ee=JavaSE)(version=1.8)) when embedding in org.apache.felix.framework
** Improvement
* [FELIX-4812] - BundleRepository can be quite CPU intensive when starting a lot of bundles

Is it required to import transitive dependencies as bundles to KURA

I am very new to OSGI and KURA. I am tackling with a problem since yesterday and I did not understand its reason.
Please, tell me if my way is wrong.
I am using dropbox-core-sdk (version 3.0.0) in my project. I have downloaded its jar and also, I have researched that it has a dependency on jackson-core (version 2.7.4). I have also downloaded its jar and I have created a bundle with dropbox-core-sdk.jar and jackson-core.jar.
Firstly, I have imported the dependencies (bundle with dropbox and jackson) and then imported my own project.
When I start my project, it throws the following exception;
java.lang.NoClassDefFoundError: javax/net/ssl/HttpsURLConnection
at com.dropbox.core.http.StandardHttpRequestor.prepRequest(StandardHttpRequestor.java:196)
at com.dropbox.core.http.StandardHttpRequestor.startPost(StandardHttpRequestor.java:70)
at com.dropbox.core.http.StandardHttpRequestor.startPost(StandardHttpRequestor.java:28)
at com.dropbox.core.DbxRequestUtil.startPostRaw(DbxRequestUtil.java:232)
at com.dropbox.core.v2.DbxRawClientV2$1.execute(DbxRawClientV2.java:100)
at com.dropbox.core.v2.DbxRawClientV2.executeRetriable(DbxRawClientV2.java:256)
at com.dropbox.core.v2.DbxRawClientV2.rpcStyle(DbxRawClientV2.java:97)
at com.dropbox.core.v2.users.DbxUserUsersRequests.getCurrentAccount(DbxUserUsersRequests.java:120)
at org.eclipse.kura.example.hello_osgi.DropBoxTransfer.<init>(DropBoxTransfer.java:37)
at org.eclipse.kura.example.hello_osgi.DropBoxUpdateJob.execute(DropBoxUpdateJob.java:20)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
I have two related questions;
When we create a bundle from a public api, should this bundle contain the transitive dependencies of the public api?
Even if I supplied the Dropbox api with its transitive dependencies, why threw the program such an exception?
Typically NoClassDefFoundError happens when a bundle loads a class that is not present in the bundle and there is not Import-Package statement for the package of the class.
When creating bundles make sure you use a bnd to auto create the Manifest with suitable Import-Package and Export-Package instructions.
I would always use the build to create a bundle from a jar. As I use maven I would use a maven plugin. See this question for some possible ways to create bundles.

LogConfigurationException when using HttpClient

When my code executes this line:
DefaultHttpClient httpclient = new DefaultHttpClient();
I'm getting this Exception:
org.apache.commons.logging.LogConfigurationException: Class org.apache.commons.logging.impl.Log4JLogger does not implement Log
does somebody know how can I solve that?
Maybe is not important (or maybe it is), but the app is a plugin for Jira, and is using Maven for the dependencies.
Thanks !
EDIT:
EDIT 2:
Could this be related with OSGi from Jira?
How can I use commons-logging in an OSGi environment?
From here:
http://wiki.apache.org/commons/Logging/FrequentlyAskedQuestions
Commons-logging was not designed with OSGi in mind. This is why it is
difficult to get commons-logging working in OSGi environments:
LogFactory loads Log implementations by name (see
Class.forName(String)). This is usually not possible in OSGi since
every bundle classloader can only see the classes a bundle defines
imports for. The bundle class loader that loads the commons-logging
bundle will not have access to user provided
commons-logging.properties files. commons-logging-api.jar contains
classes that are also included in commons-logging.jar. This is
contrary to traditional OSGi application architectures where one
bundle defines an API and other bundles provide implementations for
that API. There alternatives to using commons-logging directly in OSGi
are:
Rebundled versions that contain proper OSGi meta data are available
from Apache Felix, SpringSource and Eclipse Orbit. Using Pax logging.
Further information about this topic is available in the archives of
the commons dev ML and the felix dev ML and in Jira.
and when I try to access to the OSGi tab from Jira (going to JIRASERVER/plugins/servlet/upm/osgi#osgi) I can see an exception:
classNotFoundException: org.apache.commons.logging.impl.SLF4JLogFactory
And also I can see from the IDE (when searching for Log4JLogger class) that I have two definitions:
package org.apache.commons.logging.impl.Log4JLogger
public class Log4JLogger implements Log, Serializable {
and
package com.atlassian.extras.common.log;
class Log4jLogger implements com.atlassian.extras.common.log.Logger.Log {
this is really confusing....
I'm not sure, but could it be that the logger you configured does not correspond to the logger in your pom dependencies? Perhaps a different version?

REST client inside of OSGi application

I need to integrate a REST client into an existing OSGi application implemented using Apache Felix. The REST service is based on RESTeasy implementation (version 2.3.2.Final) of JAX-RS. I created a separate bundle with clients' dependencies, exporting required RESTeasy packages and importing them in the bundle where the client is used, but unfortunately I cannot get it working inside of the OSGi context.
I tried two different approaches. First one using the generic ClientRequest:
ClientRequest request = new ClientRequest(MyService.URL_TEST+"/stats");
request.body(javax.ws.rs.core.MediaType.APPLICATION_XML, stats);
ClientResponse<String> response = request.post(String.class);
The error that I get in this case is pretty weird:
[java] java.lang.RuntimeException: java.lang.ClassCastException:
org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor cannot be cast to
org.jboss.resteasy.client.ClientExecutor
where I it is known for sure that ApacheHttpClient4Executor implements the ClientExecutor interface.
When I try to use my own REST client wrapper around RESTeasy like this:
MyService myService = MyServiceClient.getInstance();
myService.saveStatistics(stats);
I get a different exception:
[java] java.lang.LinkageError: ClassCastException: attempting to
castjar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar
!/javax/ws/rs/ext/RuntimeDelegate.classtobundle:
//78.0:1/javax/ws/rs/ext/RuntimeDelegate.class
As far as I understand, the LinkageError most probably has to do with the way RESTeasy initializes the RuntimeDelegate using some classloader tricks, which probably fall under the restrictions of OSGi framework. I get the suspicion that the java.lang.ClassCastException mentioned first has the same source.
Is there any way to get RESTeasy working inside of OSGi?
PS: discussion about a similar issue with RESTeasy, but outside of OSGi: java.lang.LinkageError: ClassCastException
Update:
these are the libraries included into restclient bundle:
activation-1.1.jar commons-codec-1.2.jar commons-httpclient-3.1.jar commons-io-2.1.jar commons-logging-1.0.4.jar flexjson-2.1.jar httpclient-4.1.2.jar httpcore-4.1.2.jar javassist-3.12.1.GA.jar jaxb-api-2.2.3.jar jaxb-impl-2.2.4.jar jaxrs-api-2.3.2.Final.jar jcip-annotations-1.0.jar jettison-1.3.1.jar jsr250-api-1.0.jar junit-4.10.jar log4j-1.2.14.jar resteasy-jaxb-provider-2.3.2.Final.jar resteasy-jaxrs-2.3.2.Final.jar resteasy-jettison-provider-2.3.2.Final.jar scannotation-1.0.3.jar slf4j-api-1.6.4.jar slf4j-log4j12-1.6.4.jar myservice-common-0.1.0.3.jar my-service-client-0.1.0.3-SNAPSHOT.jar stax-api-1.0-2.jar xmlpull-1.1.3.1.jar xpp3_min-1.1.4c.jar xstream-1.4.2.jar
These are the exports from the restclient bundle: javax.ws.rs, javax.ws.rs.ext, javax.ws.rs.core, org.jboss.resteasy.client, org.jboss.resteasy.client.cache, org.jboss.resteasy.client.extractors, org.jboss.resteasy.client.marshallers, org.jboss.resteasy.client.core.executors, javax.xml.bind.annotation, org.jboss.resteasy.plugins.providers, org.jboss.resteasy.plugins.providers.jaxb, org.jboss.resteasy.spi
Have a look at the SpringSource Bundle Repo, it's got some very useful pre-built bundles of common libraries including the Apache HTTP Client which we are using (in conjunction with gson) to do our RESTful comms.
(unfortunately a legacy module of my project still uses OSGi, but using RESTeasy 3.0.16 now)
When I need to OSGify a dependency my preferred solution now is to wrap it using the excellent Apache Ops4j Pax Tipi project.
The project provides a preconfigured Maven setup (parent POM handles the bundling) and you just have to adapt the GAV coordinates of the original project in a Tipi sub module with a org.apache.ops4j.pax.tipi prefix and build the new bundle project which draws in the original dependency, unpacks and wraps it as OSGi bundle.
You can start from an existing Tipi sub project that best matches your project setup (dependencies, etc.) and adapt any OSGi imports/exports missing (most often, these are created automatically by the maven-bundle-plugin anyway).
This worked quite well for me as long as the original project did not contain too many exotic or malformed dependencies.
However you may run into snags like transitive dependencies using the root package, as I currently experience, which can be a real show stopper (finding out which library is a real nightmare).
Unfortunately, RESTeasy seems to be affected by this, as I get exactly the same error (default package , even after declaring non-test and non-provided dependencies as optional:
The default package '.' is not permitted by the Import-Package syntax.
Upgrading the maven-bundle-plugin to the latest release 3.0.1 yields a different error (even less helpful):
[ERROR] Bundle org.ops4j.pax.tipi:org.ops4j.pax.tipi.resteasy-jaxrs:bundle:3.0.16.Final.1 : Can not parse name from bundle native code header:
[ERROR] Error(s) found in bundle configuration
Update seems to be solved by upping Tipi version in POM to 1.4.0, testing...
Is RESTEasy mandatory ?
I personally use jersey in OSGi and it is working perfectly, both as client and server.
This problem isn't limited to RESTeasy. It also occurs with Jersey.
It is occurring because you have two copies of the JAX-RS classes on the classpath.
You can see this in the LinkageError:
[java] java.lang.LinkageError: ClassCastException: attempting to cast jar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar!/javax/ws/rs/ext/RuntimeDelegate.class to bundle://78.0:1/javax/ws/rs/ext/RuntimeDelegate.class
i.e. one copy is coming from:
D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar
and the other from the OSGI bundle.
This causes problems for the RuntimeDelegate class, which by default uses the system class loader to create the RuntimeDelegate implementation (see javax.ws.rs.ext.FactoryFinder).
The problem can also occur if the same jar is loaded via two different class loaders.
There are a couple of workarounds:
remove the jaxrs-api-2.3.2.Final.jar from the system class path
set the thread context class loader to that of your bundle, prior to making any JAX-RS calls.
The FactoryFinder will use this to load the RuntimeDelegate.
To avoid polluting your code with calls to Thread.currentThread().setContextClassLoader(myBundleClassLoader), you can wrap your JAX-RS client using a Proxy. e.g. see the Thread context classloader section of https://puredanger.github.io/tech.puredanger.com/2007/06/15/classloaders/

Package uses conflict: Import-Package: de.foo.bar; version="0.0.0"

I try to install a bundle in an OSGi environment (FUSE ESB) but do not manage to get it resolved. The error message is:
The bundle could not be resolved. Reason: Package uses conflict: Import-Package: de.foo.bar; version="0.0.0"
My bundle imports the package de.foo.bar.
The bundle which exports the package de.foo.bar does this with a 'uses' directive.
Export-Package = de.foo.bar;uses:="{other packages}";version="2.4.0"
As I understood I have to ensure that my bundle must import all other packages mentioned in the 'uses' directive of the de.foo.bar package (in the right version).
I checked this and also tried several version changes (0.0.0 and the real version numbers) but can not get it to work.
So, what does the error message realy means (maybe I understood it wrong)? What do I have to check?
Thanks for any help
Klaus
System Information:
FUSE ESB 4.2.0 (based on servicemix)
using maven-bundle-plugin 2.1.0 to generate OSGi MANIFEST header
I finally found what was wrong.
My bundle is a Spring Dynamic Module bundle and I did a mistake in the spring bean configuration (use a 'ref' instead a 'value' in a constructor-arg). Normally spring configuration errors are reported as such - I do not know why the current error resulted in the misleading message.
EDIT:
The faulty Spring configuration does not cause the uses conflict. It finally was the import of the package org.apache.log4j which is exported by different bundles (in my FUSE ESB container) and apparently was different wired to the bundles I tried to install.
Trying to solve my problem I found the article Diagnosing OSGi uses conflicts which I found helpfull to understand the problem.

Categories

Resources