RCP ClassNotFoundException with nested jars - java

I have an RCP application with the following setup:
my.plugin.jar
|-->META-INF
|-----MANIFEST.MF
|-->A1.jar
|-->A2.jar
|-->A_Dependencies.jar (Jar of jars)
|----->X1.jar,X2.jar,...Xn.jar
When I run it I get ClassNotFoundExceptions relating to the nested jars (X1.jar, X2.jar, etc...)
My Manifests contain (among other things) the following entries:
my.plugin.jar
Bundle-ClassPath: .,A1.jar,A2.jar,A_Dependencies.jar
A1.jar
Bundle-ClassPath: .,A_Dependencies.jar
A2.jar
Bundle-ClassPath: .,A1.jar
A_Dependencies.jar
Bundle-ClassPath: X1.jar, X2.jar,...Xn.jar
Export-Package: (All the appropriate packages)
How do I properly set up the manifest to resolve these? A sample error would look like:
Caused by: java.lang.ClassNotFoundException: some.package.some.classfile cannot be found by my.plugin
at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:439)

OSGi bundles nested inside another bundle are just considered ordinary jar files. Their manifest will not be picked up, and hence Bundle-ClassPath will not work. If you want to deploy all jars inside one bundle (why not deploy all bundles separately?), you need to flatten the structure so that all your dependency jars are directly under your bundle, and added to the Bundle-ClassPath of your manifest:
my.plugin.jar
Bundle-ClassPath: .,A1.jar,A2.jar,X1.jar,...,Xn.jar
From the error message, it seems your are running Equinox. I'm more familiar with Felix, and the maven-bundle-plugin, which can be configured to embed dependencies and add them to the classpath. The bundles created by the plugin are not tied to Felix, so you could take a look at that instead of manually write bundle manifests.

Related

Export-Package from OSGi bundle with bundled jars

I need some help to access exported packages for an OSGi bundle.
I have a simple OSGi bundle jar which essentially bundles some other non-OSGi jars and exports their packages. This OSGi jar is structured like:
root/
lib/
mylib.jar
META-INF
MANIFEST.MF
It's MANIFEST.MF looks as below. The package mylib.mypackage is available in the bundled jar lib/mylib.jar.
Manifest-Version: 1.0
Bundle-Name: my.osgi.bundle
....
Bundle-ClassPath: .,lib/mylib.jar
Export-Package: mylib.mypackge
I need to access the package mylib.mypackage from a plain Java application. I have tried following construct by leveraging eclipse or equinox:
/*
the installBundle() is used to load the packages & packages from myosgi.jar
because it is not directly added to the class-path
*/
....
EclipseStarter.getSystemBundleContext().installBundle("file:<<path-to>>\\myosgi.jar");
EclipseStarter.run(null);
mylib.mypackge.MySampleClass obj = new mylib.mypackge.MySampleClass();
....
The myosgi.jar is NOT added to the classpath directly and installBundle() API above is intended for class loading for myosgi.jar. Adding myosgi.jar in classpath anyway does not resolve mylib.mypackge.
However the above line fails with error ClassNotFoundException / ClassDefNotFoundError for mylib.mypackge.MySampleClass.
I need some guidance for:
How can I access the classes from the packages from myosgi.jar in a plain Java application? I am fine with initiating an OSGi runtime codefully inside my application.
Is there any way to auto-test the myosgi.jar to check if the packages exported by this jar are getting correctly resolved (in an OSGi env)? I don't have access to maven, we use ant for our builds. I would prefer to have minimal dependencies on any 3rd party jars for testing purpose.
Thanks in advance.
Regards,
Gau

Configuring pax-logging in OSGi environment built with Gradle and BND

I am trying to get Log4J2 working via Pax Logging but online docs focus on Log4J (v1). My project is Java, Gradle with BND plugin for OSGi bundles aimed at the Equinox environment.
I am using Gradle 6.8.3
I have my build.gradle file for an OSGi bundle that aims to expose logging functionality to other bundles using:
implementation 'org.ops4j.pax.logging:pax-logging-api:2.1.0'
implementation 'org.ops4j.pax.logging:pax-logging-log4j2:2.1.0'
In my BND file, I include the following imports:
Import-Package: org.apache.logging.log4j;version="2.17.1";provider=paxlogging, org.apache.commons.logging;version="[1.1.1,2)";provider=paxlogging, org.apache.logging.log4j.core;version="2.17.1";provider=paxlogging
Since my project has file appenders defined, which don't form part of the Log4J2 API, but Log4J2 Core, I therefore export the following from the same bundle to enable Log4J2 Core classes to have visibility in other bundles that depend on the logging bundle:
Export-Package: com.mycompany.loggingbundle, org.apache.logging.log4j, org.apache.logging.log4j.message, org.apache.logging.log4j.util, org.apache.logging.log4j.core;version="2.17.1", org.apache.logging.log4j.core.appender;version="2.17.1", org.apache.logging.log4j.core.filter;version="2.17.1", org.apache.logging.log4j.core.impl;version="2.17.1", org.apache.logging.log4j.spi;version="2.17.1"
Everything compiles, builds and install fine.
At runtime, I have an issue:
org.osgi.framework.BundleException: Could not resolve module: com.mycompany.otherbundle [1306]
Unresolved requirement: Require-Bundle: com.mycompany.loggingbundle
-> Bundle-SymbolicName: com.mycompany.loggingbundle; bundle-version="<hidden>"; singleton:="true"
com.mycompany.loggingbundle [1311]
Unresolved requirement: Import-Package: org.apache.logging.log4j.core; provider="paxlogging"; version="2.17.1"
at org.eclipse.osgi.container.Module.start(Module.java:434)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1582)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.incStartLevel(ModuleContainer.java:1561)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.doContainerStartLevel(ModuleContainer.java:1533)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1476)
at org.eclipse.osgi.container.ModuleContainer$ContainerStartLevel.dispatchEvent(ModuleContainer.java:1)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:340)
Hopefully some OSGi expert knows what I've got wrong because the whole reason to use Pax Logging was to avoid the need to create Log4J2 fragments and have an easier configuration for a multi-bundled environment. Perhaps there is a systematic series of things to look at to resolve this?
Update
I opened up the pax-logging-log4j2 JAR file to review its manifest and can see it doesn't export anything from org.apache.logging.log4j.core so my re-exporting it from my bundle could never provide the core packages I was hoping.
This still leaves the problem of how to get access to things like a FileAppnder elsewhere in code, but it answers the question as to what is wrong with my approach.

How to use OSGI Embed-Dependency for common 3rd party JAR-s

I have a Liferay system with several portlets. Most of these portlets have redundant JSF related JAR-s in them so I would like to remove the redundancy, and create an OSGI bundle for the commonly used JAR-s.
The idea would be that all of my portlets would use this common bundle as a dependency.
After some reading about I ended up with something similar in my maven pom:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.4</version>
<extensions>true</extensions>
<configuration>
<remoteOBR>true</remoteOBR>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.name}</Bundle-Name>
<Bundle-Vendor>${project.organization.name}</Bundle-Vendor>
<Import-Package>
!sun.reflect,......,*
</Import-Package>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
</plugin>
And I ended up with the following bundle jar:
My Manifest:
Manifest-Version: 1.0
Bundle-SymbolicName: my-common-bundle
Built-By: pjaloveczki
Bundle-ManifestVersion: 2
Bnd-LastModified: 1537882770915
Embed-Dependency: *;scope=compile|runtime
Import-Package: com.liferay.portal.kernel.exception,com.liferay.portal
.kernel.language,com.liferay.portal.kernel.model,......,org.w3c.dom.styleshe
ets,sun.misc
Tool: Bnd-1.15.0
Bundle-Name: my-common-bundle
Bundle-Version: 1.0.0
Bundle-ClassPath: .,sac-1.3.jar,...all..my..dependecies...,com.liferay.faces.bridge.api-4.1.0.jar
Ignore-Package: net.sf.cglib.proxy,..all..ignored..packages...javax.ejb
Embed-Transitive: true
Created-By: Apache Maven Bundle Plugin
Build-Jdk: 1.8.0_171
Content:
From what I can see, this is exactly what I needed, at least this is how I pictured it.
In my consuming portlet I added the following to my manifest:
Require-Bundle: my-common-bundle;bundle-version="1.0.0"
I figured something like this should work, however I am obviously wrong as when I try to deploy my portlet one of the classes is not being found provided by the common bundle:
java.lang.ClassNotFoundException: org.richfaces.webapp.ResourceServlet cannot be found by MyPortlet
On the other hand if I add the following to my common bundle:
<Export-Package>org.richfaces.webapp</Export-Package>
The class is found, but I am ended up with this:
So essentially I have the class twice once in the JAR and once flattened out, even though it's kind of starting to work.
There are several reason I don't like this approach:
I would prefer using structured jars because I consider it cleaner
Most of these jars contain configuration files that could overlap if I flatten everything out
There must be a way to use embedded jars properly since otherwise this feature would not exist
Can anyone help, what it the proper way to use these embedded jars in an OSGI without having to flatten them out?
Thank!
Peter
EDIT:
It seems that classes are being deployed fine and are resolved after I've added
<_exportcontents>!org.apache.commons.logging,*</_exportcontents>
however I am getting different types of errors which I am not getting when I put my JARs in my portlets.
Previously I was getting ClassNotFoundErrors and such, now I am getting:
java.lang.NullPointerException
at javax.faces.CurrentThreadToServletContext.getFallbackFactory(CurrentThreadToServletContext.java:79)
at javax.faces.FactoryFinderInstance.getFactory(FactoryFinderInstance.java:551)
at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:283)
at javax.faces.webapp.FacesServlet.init(FacesServlet.java:358)
java.lang.NullPointerException
at javax.portlet.faces.GenericFacesPortlet.getBridgeClassName(GenericFacesPortlet.java:193)
at javax.portlet.faces.GenericFacesPortlet.getBridge(GenericFacesPortlet.java:762)
at javax.portlet.faces.GenericFacesPortlet.init(GenericFacesPortlet.java:448)
at com.liferay.portlet.InvokerPortletImpl.init(InvokerPortletImpl.java:297)
It seems to me classes are loaded, but the JAR manifests are not being processed or something similar. Any ideas?
you can use <_exportcontents> instruction to export the content without duplication, more about it here
I would like to point out that creating fat jars is against the very idea of OSGi, also, this is going to be a nightmare to maintain when your code evolves.
Ideally you would want to have a separate bundle for each dependency. They should be deployed and maintained separately.

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.

how to fix missing requirement while the deployment of an OSGI bundle

I am trying to deploy a simple OSGI bundle (hello world) in glassfish 4.1.1 but I got the following error:
Infos: org.osgi.framework.BundleException:
Unresolved constraint in bundle com.mycompany.MavenHelloServiceImpl [324]:
Unable to resolve 324.0: missing requirement [324.0]
osgi.wiring.package; (osgi.wiring.package=com.mycompany.mavenhelloserviceapi)
at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:3974)
I don't know how to fix the missing package , I already defined "com.mycompany.mavenhelloserviceapi" as a dependency in the bundle MavenHelloServiceImpl and it is present in the dependencies folder in the bundle MavenHelloServiceImpl
Any idea how to fix this error ?!
Having com.mycompany.mavenhelloserviceapi as a dependency in your pom.xml is not enough : the pom is about compile-time dependencies. you see here an issue about a runtime dependency missing.
You should install com.mycompany.mavenhelloserviceapi in your container.
You should modify the project that contains com.mycompany.mavenhelloserviceapi as a bundle project and explicitly export this package. now deploy this bundle alongwith your hello world bundle.
Alternatively ,you can use Pax Wrap to deploy the jar that contains com.mycompany.mavenhelloserviceapi to glassfish without modifying the project.

Categories

Resources