Using a javax.usb.properties file in OSGi bundle - usb

I'm trying to access an USB device from an OSGi container. I am using javax.usb, with usb4java as its underlying implementation. I have converted all relevant JAR files to OSGi bundles by modifing their MANIFEST.MF files. This works ok, where finding classes is concerned.
I need to tell javax.usb what implementation is used by specifying a javax.usb.properties file on the classpath. Problem is: the classpath is seperate for each bundle. How do I put the properties file on the classpath of the javax.usb bundle?

You can modify the classpath of a bundle using a Bundle fragment:
Sometimes, fragments are used to 'patch' existing bundles:
Host bundle:
Bundle-SymbolicName: the.host
Bundle-ClassPath: patch.jar,.
Fragment:
Bundle-SymbolicName: the.host.fragment
Fragment-Host: the.host
In this scenario, the patch.jar is not available in the host bundle
itself, but may be supplied by the fragment. Since it is ahead of '.'
it will allow classes to be preferentially loaded from the fragment
instead of the host.

Related

Exported package not resolved in Eclipse Plugin

I'm using org.slf4j:slf4j-api:1.7.30 and ch.qos.logback:logback-classic:1.2.3 in one of my bundles (Maven <packaging>eclipse-plugin) via the project's Target Platform definition (i.e. another Maven project with <packaging>eclipse-target-definition).
The slf4j-api JAR's MANIFEST.MF contains:
...
Import-Package: org.slf4j.impl;version=1.6.0
The logback-classic JAR also contains the package org.slf4j.impl and its MANIFEST.MF contains:
...
Export-Package: ...
....................., org.slf4j.impl;versi
on="1.7.25";uses:="org.slf4j,org.slf4j.spi"
...
If I add:
Import-Package: ...
...
org.slf4j,
ch.qos.logback.classic,
org.slf4j.impl
to my bundle's MANIFEST.MF` only the first two are resolved. For the latter Eclipse shows the error:
Bundle 'ch.qos.logback.slf4j' exporting package 'org.slf4j.impl' is unresolved
and the ch.qos.logback.classic_1.2.3.v20200428-2012.jar in my bundle's Plugin-Dependencies folder shows only ch.qos.logback.classic package and sub-packages thereof.
Major question:
Why is org.slf4j.impl not considered exported and hence cannot be resolved?
Minor questions:
Where does the Bundle-SymbolicName in the error message come from?
Where does the qualifier v20200428-2012 of logback-classic's JAR in the Target Platform definition come from? It's MANIFEST.MF doesn't contain this, but just Bundle-Version: 1.2.3.
Why does slf4j-api import org.slf4j.impl? Shouldn't rely an implementation on an API and not vice versa?
UPDATE
I found answers to 2. and 3. (not the first that this happens while writing an SO question :): It's from Eclipse itself: https://ftp.fau.de/eclipse/releases/2021-03/202103171000/plugins/
And this also leads to an answer for 1.: I'm actually not using the orignal slf4j and logback JARs but those from Eclipse and ch.qos.logback.slf4j's MANIFEST.MF contains:
...
Export-Package: org.slf4j.impl;x-internal:=true;version="1.7.30";uses:
="org.slf4j,org.slf4j.spi"
...
So 5. How to get the package org.slf4j.impl exported/imported nevertheless?

How to make a resource file visible to all bundles in OSGi?

I'd like to include a resource file (e.g. some xml config file) in my bundle and make it visible to all other bundles in the container. Is it possible without using the Fragment-Host manifest header? I'd like this resource file to always be visible in the classpath of all bundles running alongside my bundle, even those that do not exist yet, but will potentially be added in future.
EDIT:
To clarify - that resource must be available passively, i.e. the other bundles should be able to find it in their classpath, and not by refering to any special API or service of my bundle.
Some more background - my environment is a bit messy but I have no control over it and cannot change its existing bundles. The only way I can modify it is by adding my own bundles. That environment includes several copies of the ch.qos.logback.classic bundle. When logback starts up, it looks for specific XML config files in the classpath. If it doesn't find any of them, then its default behaviour is to print everything to stdout with debug level. This environment was previously used to host a GUI application so it didn't matter that much before, but now I am trying to adapt it so I can use some of its functionality in headless mode. So now it becomes important to me to be able to configure it in such a way that only warning and errors are printed to the console.
In general, no you cannot do this. Class-space isolation is at the heart of OSGi, but you want to put a resource in the class loader of one bundle and make it visible to all other bundles. That's not OSGi, it's the global application classpath.
The only thing you can do to add to the internal classpath of a specific bundle is to write a fragment which attaches to that bundle. A fragment can attach to multiple host bundles, but only if those hosts have the same symbolic name, i.e. because they are different versions of the same bundle. See OSGi R6 Core Specification, section 3.14.
You did however state that the bundles you want to attach are all copies of ch.qos.logback.classic. If that means they all have that exact symbolic name then perhaps a fragment will work after all.
You can not change the classpath of other bundles this way.
What you can do is retrieve the classloader of your bundle from your bundleContext. You can give this classloader to another bundle to retrieve your resource.
ClassLoader cl = context.getBundle().adapt(BundleWiring.class).getClassLoader();
Another option is to give the other bundle the URL of the resource.
As long as the resource is on the classpath, any bundle can access the resource if it can get hold of the class loader of the bundle that contains the resource.
For example:
ClassLoader classLoaderOfBundleWithResource = ...
classLoaderOfBundleWithResource.getResourceAsStream("org/example/resource.xml");
From a maintenance and API point of view, I would not recommend exposing a resource that way. Java types are much better suited therefore. Instead, let the resource bundle export a class that gives clients access to the contents of the resource.
For example:
public class XmlDocumentProvider {
public InputStream openDocument() {
return getClass().getResourceAsStream("resource.xml");
}
}
Assuming that both the resource.xml and the XmlDocumentProvider reside in the same package, openDocument will return the resource content just like in the first example.

Bundle Manifest dependencies

Recently I have been assigned to improve the structure of inter-dependencies that an Eclipse plugin has. Let's call it "core" and assume that the following manifest describes this eclipse plug-in where all the other plug-ins point to in order for them to access services (RMI, OSGi services). These services are reachable by using either "rmiservices.jar" (Maven generated artifact) and another eclipse plug-in called "org.osgiservice". Other bundles reference "core" bundle and use the Export-Package declaration in order to use these services.
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: core
Bundle-SymbolicName: coreid;singleton:=true
Bundle-Version: 0.0.1
Bundle-Activator: org.sample.Activator
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
Export-Package: org.sample.api,
org.rmiservice,
org.osgiservice
Bundle-Localization: plugin
Bundle-ClassPath: lib/rmiservices.jar
Require-Bundle: org.osgiservice;bundle-version="1.0.0";visibility:=reexport
Questions that arise in order to improve the dependency structure are:
The "core" plug-in includes within its generated jar the rmiservices.jar. Would it be better having rmiservices.jar as a separate bundle instead?
rmiservices.jar is a maven generated artifact. Would there be a benefit moving it to a P2 repository and expose it inside a target definition?
You should put the jar into its own bundle. If your application grows, you might need to access that library from different bundles. With local jars you risk different versions being loaded by different bundles (due to those local jar copies), leading to very strange errors at runtime.
If the jar is available in P2, then you can easily build your complete application using Tycho. The automation and reproducability of the build process is well worth the shortly longer build time.

Glassfishv3 not adding jars in ear to the classpath

I have been working on this problem for one whole day but in vain without any effective solution.
I have an ear file packaged with an ejb and a handful of jar files (including hibernate and the other dependent jar files).The ejb is stateless and enabled as a web service.
The ear file has been packaged using maven and has the below structure
ear->projectrelatedejb.jar
->hibernate.jar
->otherdependent. jar
->META-INF/application.xml
->META-INF/manifest.mf
The application.xml and manifest file are automatically generated by maven when I do a package.
When I deploy this ear file on glassfish it gets deployed with the ejb methods being accessible using web services. However when accessing the application (using soapui),
the ejb methods that perform some database functionality using hibernate throw java.lang.NoClassDefFoundError for the hibernate api during runtime.
It is obvious from the error that the hibernate jars are not on the classpath during runtime but since the jars are within the ear Glassfish should have
added it to the application classpath.
I tried various options like adding the classpath entries to the manifest.mf during the package (by using the element addClasspath with the maven-ear-plugin) which didn't do any good.Also with Glassfish we cannot add the dependent jars as modules to the application.xml unless the jars are application client jars
(Glassfish wouldn't deploy the ear file if the application.xml has the dependent jars declared as modules).
I also tried placing the jars in the lib directory within the ear (which isn't actually required) and with the manifest Class-Path header referencing the jars in the lib directory which also didn't fix the problem.
The quick and dirty fix which I can do to get this working is to place the hibernate and the other the dependent jars in Glassfish's lib directory.However,this is a bad practice
and I am somewhat reluctant to do it.
I would really appreciate if someone can provide me with a working solution to this problem.I have gone through the net looking for this problem
but couldn't find any solution.
Wondering if its a bug with glassfish or does glassfish need something special to reference the jars in an ear.
Thanks in advance.
I found a similar problem which is discussed here: http://www.tricoder.net/blog/?p=59.
Simply put, try putting the libraries in EAR/lib directory and according to JEE5 spec, glassfish will add them to class path automatically.
I used Server Library option to deploy application JARs and it worked for me.
Right click on your EAR-> Properties -> Libraries-> Add Library -> Create -> give name and change type in Library Type to Server Libraries then add JARs that should be deployed and confirm.
I work with NetBeans 7.0.1 and GlassFish server 3.1
When you say you added classpath entries to manifest.mf, which manifest.mf do you refer to? The one in ear-root/META-INF/manifest.mf ? Try adding a META-INF/MANIFEST.MF to your ejb module with Class-Path entries!

Embedding JARs into the OSGi bundle with maven-bundle-plugin

I’m trying to embed some JARs into single OSGi bundle using the feature of maven-bundle-plugin
The thing that worries me is that all packages of embedded JARs are put into the Import-Package header of the generated MANIFEST.MF.
If I specify explicitly to use only the packages I need, like in the following snippet:
Import-Package: org.osgi.framework
The build fails with BND error (unresolved references).
So, the question here is how can I build the bundle with embedded JARs with "Import-Package" header I need?
All the packages that are imported in your classes will be imported by bnd. Perhaps you do not want those packages imported because you know that at runtime you won't be needing them. If you cannot stop bnd from importing them, you can make them optional so that your bundle will still resolve even if they are not supplied by another bundle (at wire time). Try to add this:
<Import-Package>*;resolution:=optional<Import-Package>
To your maven bnd configuration in maven.
One possible reason why you are seeing "unexpected" packages in Import-Package header is the following:
A general good practice that supports collaboration model in OSGi is to import all packages that you export -- see this blog post by Peter Kriens for detailed explanation why. Bnd (and hence also maven-bundle-plugin) follows this practice by default and automatically imports all exported packages. Therefore you should first check your Export-Package header and make sure that you export only the packages you want.
Also if you want to export packages from the embedded dependencies then you should be careful to avoid duplication inside your bundle -- see section Embed-Dependency and Export-Package of the maven-bundle-plugin documentation.
You should use Bundle-ClassPath if you want to make classes available inside a bundle that contains JARs e.g.
Bundle-ClassPath: foo.jar,other.jar
Import-Package: org.osgi.framework,org.other.imported
You'll need to list the classes that foo.jar and other.jar import/use, but you won't need to list any of the packages in foo.jar or other.jar unless you're actually exporting them.
You can remove some packages from import-package scope when you embed a JAR into your bundle:
<Import-Package>![package_name9]<Import-Package>
inside pom.xml or if you use external *.bnd files:
Import-Package: ![package_name]