I was using Felix as a embedded application as explained in,
How to start and use Apache Felix from code?. What I want to do is dynamically load jar files from my host application via OSGi and invoke methods of implementation classes.
So I have following three maven projects
1) A maven project which has an interface. And the package of this interface is exported. ---> ProjA .
2) A implementation project --> ProjB, another maven project which import ProjA as a maven dependency and implement interface on it with a concrete class. Also in this project I do OSGi import-package for ProjA interface package. Also here I register my implementation on OSGI via activator.
3) Then ProjC which is the hosted application. What I do there is,
HostActivator activator = new HostActivator();
List<Object> list = new LinkedList<Object>();
list.add(activator);
map.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list);
Felix f = new Felix(map);
f.start();
Bundle a = f.getBundleContext().installBundle("file:C:/ProjA.jar");
Bundle b = f.getBundleContext().installBundle("file:C:/ProjB.jar"); ); // dirty path ;)
b.start();
ServiceReference sr = activator.getContext().getAllServiceReferences(MyInterface.class.getName(), "(" + "osgi-device-name" + "=*)")[0];
MyInterface dictionary = (MyInterface) activator.getContext().getService(sr);
dictionary.doAction();
Everything works fine until cast. There I can see following error,
Exception in thread "main" java.lang.ClassCastException: projB.MyImplementation cannot be cast to projA.MyInterface
at MyHostApplication.MyMainClass.main(MyMainClass.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Can anyone help me on this, for me this seems like a bug on felix.
ProjA is on the classpath of your main project (that opens the embedded OSGi container) and it is also installed into the embedded OSGi container as a bundle. When ProjB is resolved, it wires to the ProjA bundle, so it implements the interface that is coming from the installed projA bundle.
When you try to cast the result object, you try to cast to the interface that is on the classpath of the main project. That is a different interface that the ProjB bundle implements as it implements the interface from projA bundle.
You should not install ProjA as a bundle into the OSGi container. You should be sure that ProjB bundle can resolve. To do that, you should add projA as a system package to the embedded OSGi container.
another way to solve this problem is using export tag in maven maven-bundle-plugin or manifest file
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
<Export-Package>come.example.myInterface</Export-Package>
<Bundle-Activator>come.example.Activator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
and did'nt forget
map.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "come.example.myInterface; version=0.0.1");
Related
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
I am getting problem when tried to start bundle.
Project structure:
--Main project
- sub project 1
- sub project 2
src/main/java/util
- sub project 3
- bundle project 1
- bundle project 2
- bundle project 3
When I compiled main project using Maven install command in eclipse, it's successfully compiled. So, for testing bundle, I have downloaded felix distribution package. I am install bundle successfully but I am not able to start. I getting error dependency cannot be resolved
here is my bundle pom file
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Export-Package>
com.test.sub_project_3.step,
com.test.sub_project_3.step2
</Export-Package>
<Import-Package>*
org.osgi.framework,
org.osgi.util.tracker,
com.test.sub_project_2.util
</Import-Package>
<Embed-Dependency>
slf4j-api;scope=compile
</Embed-Dependency>
<Bundle-Activator>com.test.sub_project_3.osgi.Activator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
I am getting error for "com.test.sub_project_2.util cannot be resolved". com.test.sub_project_2 it'sussfully compiled and packag name it's also correct but still I don't understand why I am getting error.
You must understand that OSGi and Maven are different tools with different strategies concerning package resolution.
At design time, maven strategy is used. You indicate your dependency in your POM file. Moreover in your case you used the maven bundle plug in , so you can constrain which package needs to be imported at runtime by your bundle (your import-package section). So in your case, all dependencies are present at design time because your project compiles. But it not implies that the runtime resolution will work, because at runtime OSGi resolution is used.
In your case the error message indicates that your deployed bundle cannot find at runtime the com.test.sub_project_2.util in order to be executed. This is because no bundle present in the OSGi distribution provides the package. So you can bundlify the project that contains the package and deploy it in your OSGi distrib. If you do that the package is now available at runtime.
In a simplistic way you can think as this problem in this way: The classpath used for your bundle is different at design and runtime. And you cannot assume a causality relation between them.
Regards
I am trying to deploy a bundle in apache-karaf 3.0.3 which contains certain number of third party jar files which i am embedding inside,
since the third party jar files are non OSGi bundles. Out of which one jar file contains an import statement in a java file which doesn't exist
anymore in the latest version of jar file.(I didn't have the olderversion of jar file).
e.g: jar file 1
- Class1
- import com.java.test.io
While deploying my application bundle with the jar files i am facing an error.
Error executing command: Error executing command on bundles:
Unable to execute command on bundle 391: The bundle "com.test.example.bundle_0.1.0.SNAPSHOT [391]" could not be resolved.
Reason: Missing Constraint: Import-Package: com.java.test.io; version="0.0.0"
I am trying to replicate the scenario with simple java application, it works as expected.
My assumption is that karaf will scan all the import statements and check whether there is a proper export package(package level permission)
exists for the appropriate import statment.
Can anybody explain why java application runs and in karaf it fails?
pom.xml
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>mybundlename</Bundle-SymbolicName>
<Embed-Dependency>jar1,jar2,jar3,jar4</Embed-Dependency>
</instructions>
</configuration>
</plugin>
</plugins>
What matters is not the package import in your class but the package import in your bundle's MANIFEST.
If your code works outside of an OSGi container, then it means that the imported package is not required at runtime in your particular usage scenario. Or it is not required at all and should be cleaned up.
You either have to deploy a bundle to satisfy the import or you have to suppress the addition of the import of com.java.test.io when building your bundle. With the maven-bundle-plugin you can achieve this like so:
<instructions>
<Embed-Dependency>...</Embed-Dependency>
<Import-Package>
!com.java.test.io.*,
*
</Import-Package>
</instructions>
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
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).