How to dynamically load Java classes at Runtime in OSGI framework? - java

We are performing a POC in our project, where in we send SOAP based request and correspondingly get a SOAP response from a web service. We aim to leverage webservices template (client side API) provided by spring framework in our application. As per our architecture, we create an OSGI compliant bundle (for our code that uses webservices template API to interact with the web service) which is then deployed into the Apache Felix container. We have also installed all the dependent OSGI compliant bundles in the Felix container so that all the dependencies are resolved.
As per the webservices template, the default Web Service Message sender is HttpUrlConnectionMessageSender which is dynamically loaded at run time by the class loader. As per my understanding, we are getting the below exception because the Felix container is not able to load the class from the dependent OSGI bundle (web services bundle contains the HttpUrlConnectionMessageSender).Please refer to the exception logs below.
* org.springframework.beans.factory.BeanInitializationException: Could not find default strategy class for interface [org.springframework.ws.transport.WebServiceMessageSender]; nested exception is java.lang.ClassNotFoundException:org.springframework.ws.transport.http.HttpUrlConnectionMessageSender at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategies(DefaultStrategiesHelper.java:126)
at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategies(DefaultStrategiesHelper.java:90)
at org.springframework.ws.client.core.WebServiceTemplate.initMessageSenders(WebServiceTemplate.java:320)
at org.springframework.ws.client.core.WebServiceTemplate.initDefaultStrategies(WebServiceTemplate.java:306)
at org.springframework.ws.client.core.WebServiceTemplate.<init>(WebServiceTemplate.java:143)
at test.soapservice.service.SOAPServiceImpl.<init>(SOAPServiceImpl.java:40)
at test.soapservice.service.SOAPServiceActivator.start(SOAPServiceActivator.java:17)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
at org.apache.felix.framework.Felix.startBundle(Felix.java:1895)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
at org.apache.felix.gogo.command.Basic.start(Basic.java:729)
Caused by: java.lang.ClassNotFoundException: org.springframework.ws.transport.http.HttpUrlConnectionMessageSender
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:211)
at org.springframework.util.ClassUtils.forName(ClassUtils.java:164)
at org.springframework.ws.support.DefaultStrategiesHelper.getDefaultStrategies(DefaultStrategiesHelper.java:114)
As per my understanding,Felix container is unable to dynamically load the class using ClassUtils.forName() which exists in another bundle. I see this as a collaboration issue where the current bundle has a different class loader as opposed to class loader of dependent bundle.
Did someone from this community have encountered the same exception? If yes, then what were steps taken by you to resolve the run time class dependency? Please share your thoughts/pointers to resolve the above issue.A quick response would be highly appreciated and may help us to make our POC successful.
Thanks in advance,
Mridul Chopra

Classloading in the form of Class.forName() is not a problem in any OSGi container. You problem here is that the MANIFEST.MF file does not contain the right import declarations. One bundle should export the org.springframework.ws.transport package, while your bundle should import the same package.
If you are using Maven to build your bundle, you can use the Felix Bundle Plugin to generate the right manifest information.
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Private-Package>my.private.package.*</Private-Package>
</instructions>
</configuration>
</plugin>
</plugins>
This should inspect your code and add imports for anything that is not inside your "private" package scope. One other thing you should do to make this work is to set the packaging type to bundle.
<packaging>bundle</packaging>
But, the examples above is when you are using Maven as a build tool. If you are using Gradle, you can use the Gradle OSGi plugin to build manifest. Or, if using Ant you can use SpringSource Bundlor project (btw, which also has a Maven plugin).

Related

"cannot be cast to org.osgi.framework.BundleActivator" when declaratively starting OSGi bundle

There's this kind of legacy Spring based java application that runs within a container. I'm trying to provide some OSGi plugin functionality by embedding Apache Felix within this already-existing application. I'm declaratively starting the framework, by following this:
https://dotcms.com/blog/post/navigating-osgi-extending-your-software-to-embed-an-osgi-framework
In case the link doesn't work, what I'm doing is importing Felix as a maven dependency, using the ServiceLoader to get a reference to the framework factory, starting a framework, and loading/starting all bundles in a specific directory.
The bundle is created via the Felix Bundle plugin, with the following configuration:
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>com.example</Export-Package>
<Import-Package>!*</Import-Package>
<Bundle-Name>${project.description}</Bundle-Name>
<Bundle-Activator>com.example.Activator</Bundle-Activator>
<Embed-Transitive>true</Embed-Transitive>
</instructions>
</configuration>
As for the dependencies:
<dependencies>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>6.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
So as you can see, it doesn't import anything (it's the only bundle in the whole application, the servlet itself is not an OSGi bundle). Since the application is not an OSGi bundle, I cannot export the framework classes from it, Felix must be embedded in the bundle itself.
I cannot get the bundle to start. This is what I get (stacktrace has been redacted a bit):
14:43:52,981 ERROR [con.example.Plugin] (Initialization Thread) Failed to start bundle com.example: org.osgi.framework.BundleException: Activator start error in bundle com.example.Plugin[2].
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2448)
at org.apache.felix.framework.Felix.startBundle(Felix.java:2304)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:984)
Caused by: java.lang.ClassCastException: com.example.Activator cannot be cast to org.osgi.framework.BundleActivator
at org.apache.felix.framework.Felix.createBundleActivator(Felix.java:4744)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2379)
... 35 more
I've managed to reduce this to the possible fact that there are two instances of the BundleActivator interface: one in the bundle's classloader and another one in the Frameworks' ModuleClassLoader. At least I think that's what's going on.
How do people usually work around this? I've tried creating another bundle that just exports the framework for the initial bundle to import, but that one suffers from the same error when it starts. Refactoring the spring app to be overall OSGi capable is out the question (that's why I'm using Felix and not Equinox).
Bundles must not have their own internal copy of the OSGi Framework classes (i.e. the package org.osgi.framework and its subpackages). They must import those packages from the System Bundle.
The cause of the ClassCastException is that in Java the identity of a class is the combination of its fully-qualified name and the ClassLoader that defined it. If you define the BundleActivator type in multiple ClassLoaders — which is what happens when you have copies of it inside bundles — then they are considered different types.
try to change dependency framework scope from compile to provided

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?

Vaadin/OSGi : ScssStylesheet class not found

I use vaadin and OSGi to create a modular application. After a long fight, I was able to deploy successfully the application. However, I can't load the style. When I try to access to
http://localhost:8080/myapp/VAADIN/themes/myTheme/styles.css
I got the following exception:
exception
javax.servlet.ServletException: Servlet execution threw an exception
root cause
java.lang.NoClassDefFoundError: com/vaadin/sass/internal/ScssStylesheet
com.vaadin.server.VaadinServlet.serveOnTheFlyCompiledScss(VaadinServlet.java:957)
com.vaadin.server.VaadinServlet.serveStaticResourcesInVAADIN(VaadinServlet.java:790)
com.vaadin.server.VaadinServlet.serveStaticResources(VaadinServlet.java:760)
com.vaadin.server.VaadinServlet.service(VaadinServlet.java:257)
com.vaadin.server.VaadinServlet.service(VaadinServlet.java:201)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
root cause
java.lang.ClassNotFoundException: com.vaadin.sass.internal.ScssStylesheet
org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:501)
org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421)
org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412)
org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)
java.lang.ClassLoader.loadClass(ClassLoader.java:356)
com.vaadin.server.VaadinServlet.serveOnTheFlyCompiledScss(VaadinServlet.java:957)
com.vaadin.server.VaadinServlet.serveStaticResourcesInVAADIN(VaadinServlet.java:790)
com.vaadin.server.VaadinServlet.serveStaticResources(VaadinServlet.java:760)
com.vaadin.server.VaadinServlet.service(VaadinServlet.java:257)
com.vaadin.server.VaadinServlet.service(VaadinServlet.java:201)
javax.servlet.http.HttpServlet.service(HttpServlet.java:722)[/code]
com.vaadin.sass.internal.ScssStylesheet class is offered by vaadin-theme-compiler-7.x.x.jar but the server doesn't import this package. Then, I "hacked" the vaadin server bundle by adding DynamicImport-Package: * in vaadin server's MANIFEST. The previous exception does not appear but I got the same for org.w3c.css.sac.CSSException.
Note that by deploying the application .war file on tomcat, I had no error and the app looks like I expected (because all libraries are added under WEB-INF/lib and tomcat add them on the class loader). So, the problem is pure OSGi.
based on the Vaadin 7.1.3 bundles, the following changes need to be made:
import com.vaadin.theme-compiler in com.vaadin.server
import org.w3c.css.sac in com.vaadin.shared.deps and com.vaadin.theme-compiler
provide a ScssStylesheetResolver via ScssStylesheet.setStylesheetResolvers which loads from bundle resources, not (only) filesystem / classloader as the default implementation.
if you import parts of the original themes in your theme, consider creating it as a fragment to com.vaadin.themes, which in turn can then be used as a base for the ScssStylesheetResolver
This sounds like a packaging bug in Vaadin which should be reported to the authors. If the core Vaadin bundle needs to access classes from the package com.vaadin.sass.internal then it should include that in its Import-Package header.
With respect to org.w3c.css.sac, you don't specify which bundle throws an error but it sounds like the same kind of problem. Whichever bundle uses that package should import it via Import-Package.

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/

OSGi: Apache Felix Maven Bundle Plugin and Javadoc

I'm using the Apache Felix Maven Bundle Plugin to generate the OSGi metadata.
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
You can configure which packages are exported or you can use the default, which excludes packages such as *.impl.* and *.internal.*.
Is there a way to generate Javadoc only for the exported packages without having to duplicate this information?
Edit:
My current approach is that I set the excludePackageNames property (a Maven Javadoc Plugin property) manually and thus duplicate this information:
<excludePackageNames>*.internal.*:*.impl.*</excludePackageNames> <!-- used by Javadoc plugin --><!-- TODO: get this from OSGi meta data-->
http://sourceforge.net/p/drombler/drombler-oss-parent/ci/default/tree/pom.xml#l64
The Apache Maven bundle plugin is based on bndlib, which has its own plugin model. If a maven plugin model has class visibility to other maven plugins then it is easy to get this information. In the instructions in the pom register a plugin:
<instructions>
<_plugin>com.example.MyPlugin</_plugin>
</instruction>
In this bnd plugin, implement the AnalyzerPlugin interface
boolean analyzeJar(Analyzer analyzer) throws Exception {
doJavadoc( analyzer.getExportedPackages().keySet() );
}
I am not that familiar with maven plugins, since bnd will do dynamic class loader (yuck), it must be able to see your code.
I guess the best thing would be, if the Maven Bundle Plugin would provide a goal to generate the needed information.
I filed a new issue: https://issues.apache.org/jira/browse/FELIX-4181

Categories

Resources