1

I am making a very simple app in which I try to init a spring bean using osgi (apache felix). I manage to read the spring-beans.xml file I include in the bundle with code like that:

    ApplicationContext springContext = new GenericApplicationContext();
    InputStream in = thisBundle.getEntry("/spring-beans.xml").openStream();
    DefaultListableBeanFactory beans = new DefaultListableBeanFactory(springContext);
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beans);
    reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
    reader.loadBeanDefinitions(new InputStreamResource(in));
    beans.preInstantiateSingletons();
    in.close();
    return springContext;

Where this is the content of the spring-beans.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
<bean id="crazyClass" class="foo.bar.osgi.SpringInstantiatedBean">
    <property name="name" value="YES" />
</bean>
</beans>

The SpringInstantiatedBean class has only one prop (name) and a getter/setter for it. This is all fine if I run it locally through a main class, although the path for locating the spring-beans.xml file is a bit different. However, when I do put this code on felix, here is what I get in my Activator:

Jun 7, 2012 4:37:40 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from resource loaded through InputStream
Jun 7, 2012 4:37:41 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2778c490: defining beans [crazyClass]; root of factory hierarchy
org.osgi.framework.BundleException: Activator start error in bundle foo.bar.osgi.OSGIProject [91].
    at org.apache.felix.framework.Felix.activateBundle(Felix.java:2027)
    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)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137)
    at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:82)
    at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477)
    at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)
    at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
    at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183)
    at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120)
    at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:89)
    at org.apache.felix.gogo.shell.Console.run(Console.java:62)
    at org.apache.felix.gogo.shell.Shell.console(Shell.java:203)
    at org.apache.felix.gogo.shell.Shell.gosh(Shell.java:128)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137)
    at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:82)
    at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477)
    at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403)
    at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
    at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183)
    at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120)
    at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:89)
    at org.apache.felix.gogo.shell.Activator.run(Activator.java:75)
    at java.lang.Thread.run(Thread.java:662)
Caused by: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [foo.bar.osgi.SpringInstantiatedBean] for bean with name 'crazyClass' defined in resource loaded through InputStream; nested exception is java.lang.ClassNotFoundException: foo.bar.osgi.SpringInstantiatedBean
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1262)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:576)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1331)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:897)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:566)
    at foo.bar.osgi.Activator.readSpringConfigAndInitContext(Activator.java:54)
    at foo.bar.osgi.Activator.start(Activator.java:29)
    at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:641)
    at org.apache.felix.framework.Felix.activateBundle(Felix.java:1977)
    ... 32 more
Caused by: java.lang.ClassNotFoundException: foo.bar.osgi.SpringInstantiatedBean
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at org.springframework.util.ClassUtils.forName(ClassUtils.java:257)
    at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:417)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doResolveBeanClass(AbstractBeanFactory.java:1283)
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1254)
    ... 40 more
org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [foo.bar.osgi.SpringInstantiatedBean] for bean with name 'crazyClass' defined in resource loaded through InputStream; nested exception is java.lang.ClassNotFoundException: foo.bar.osgi.SpringInstantiatedBean

The full stack trace just shows to me that at the moment of resolving the beans, spring internals can't find my bean class, which is weird because I can instantiate it in my Activator with no problem, like that:

SpringInitializedBean bean = new SpringInitializedBean();

This only suggests it is a classloader problem but is it possible that the classloader for the BeanFactory can't access my classes? If so, how do I make it see my classes? In addition, here is an excerpt of my pom.xml where I configure the maven bundle plugin:

           <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.2.0</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-Activator>foo.bar.osgi.Activator</Bundle-Activator>
                        <Export-Package>foo.bar.osgi.osgi*</Export-Package>
                        <Private-Package>foo.bar.osgi*</Private-Package>
                        <Import-Package>*</Import-Package>
                        <Include-Resource>src/main/resources/spring-beans.xml</Include-Resource>
                    </instructions>
                </configuration>
            </plugin>
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
Nikola Yovchev
  • 9,498
  • 4
  • 46
  • 72
  • Does your private and export packages conflict? I don't know how the plugin will handle that pattern. – JustinKSU Jun 07 '12 at 15:02
  • 2
    Try this http://stackoverflow.com/questions/8039931/how-to-use-a-spring-bean-inside-a-osgi-bundle/8067534#8067534 – Robin Jun 07 '12 at 15:30

2 Answers2

0

Your problem seems to be that Spring tries to load the bean using Class.forName or similar methods. It assumes that the class is visible from the system classloader. Due to the fact that OSGi has strong separation of classes with separate classloaders for each bundle, this assumption is not true.

Where is BeanFactory located? Is it in the system classpath of the OSGi framework, or is it packaged as a bundle itself? If it is in a bundle, you can try playing with the Import-Package: and Export-Package clauses in the manifests of the two bundles as a temporary solution.

pooh
  • 652
  • 3
  • 2
  • Yep, I found out that the BeanFactory tries to load the classes using the OSGI framework classloader, but seems like my internal classes are not exposed yet during the start method of the Activator, so it can't find them. If I set the BeanFactory's classLoadepri manually to be my Bundle's classloader, then it fails to get the Spring classes. I am a bit mad :) – Nikola Yovchev Jun 08 '12 at 22:19
  • Setting the BeanFactory classloader to be the bundle classloader is a step in the right direction. Now why doesn't it find the spring classes? Could it be that your bundle doesn't import them in its manifest? What is the exact error? – pooh Jun 09 '12 at 19:27
  • I found myself fighting the same problem today. Spring not seeing my own bundle's classes using Spring annotated configs. The solution is either to not use Spring or OSGi. – anydoby Oct 06 '17 at 12:47
0

Two cheap things to try to get some clarity:

  • Use a Dynamic Import It's a bit of a last resort, effectively bypassing modular classloading, but it will tell you if this is indeed your problem.
  • Try it in Equinox instead of Felix. Felix is a bit more strict than Equinox. I know that Spring and Equinox work fine in Virgo. Again, in itself this won't solve your problem, but it will narrow things down a bit.
Frank Lee
  • 2,728
  • 19
  • 30