Spring component-scan in OSGi finds nothing - spring

Using the Spring-Context MANIFEST definitions, I'm trying to do a component-scan to search packages for Spring annotated beans. My Spring XML configuration looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
<!-- Scans the classpath of this application for #Components to deploy as
beans -->
<context:component-scan
base-package="com.some.other.module.one,com.another.module.two" />
<context:annotation-config />
....
</beans>
In the MANIFEST, I import the packages containing the classes with Spring annotations. However, when I inspect the ApplicationContext, it doesn't have any of the annotated beans in it.
I believe this is happening because the classpaths we're scanning are in different bundles. Those bundles don't directly importing the packages with the classes that have Spring annotations in them. What's confusing is why Spring doesn't pick up the classpath of the main bundle that the component-scan is started from? It seems as if it is using the classpath of each bundle when it's doing a classpath scan. Is there a way to get the classpath scan to use the classpath of the bundle the scan starts in?
Edit
As Danail Nachev said below, when Spring does a classpath scan, it happens only within the module that the classpath is happening in. The work around is to use:
Put your configurations per module in a Spring 3 #Configuration bean.
Use an XML file in your top level bundle that initializes the #Configuration bean.
In the top level #Configuration bean use the #Import to import the other configuration files.
Make sure to Require-Bundle in your MANIFEST to ensure the configuration you're importing is available.

OSGi is all about being modular, so it makes great deal to have clear separation between the bundles. If Spring can go and unite them under single ApplicationContext, will not be different than usual Spring application, where everything is available in single classpath. Something like this.
What's happening is that each bundle receives its own ApplicationContext. These ApplicationContexts can exchange beans using OSGi Service Registry. You need to mark beans as exported and import them in others ApplicationContexts, otherwise they are not visible to one another.
This should explain why you cannot configure everything with single Spring context and expect that starting from one bundle it would go and find all beans. Spring context scans only single bundle and optionally can import/export beans as OSGi services.
Interpreted from here: Chapter 8. Packaging and Deploying Spring-based OSGi applications

Related

Spring config Import resource with exclusions

I am using Spring to configure my application and wish to import a particular resource A. Now, resource A has another tag in it which includes resource B. But, I wish to include resource A only by itself without its subsequent imports. Is there any way to do so?
For eg, here is my configuration:
<?xml version="1.0" encoding="UTF-8"?>
....
...
<import resource="A.xml" >
Here is resource A:
<?xml version="1.0" encoding="UTF-8"?>
....
....
<import resource="B.xml" >
As mentioned, I need to import A.xml and exclude B.xml. Is it possible to do so?
Thanks.
I don't think it is possible in Spring (as of now).
Spring will load all the bean definitions found in Application-context.xml or similar file into it's container.
You can either import all beans from an xml or completely ignore. This can be achieved by using #Profile annotation.
OR You need to split your bean definition xmls.

Dealing with two different values of a property (cloud and default) in cloudfoundry and the #Value annotation

I am in reference to Spring's #Value annotation as documented here: #Value and Spring profiles.
I need to be able to have different values for a given property such as:
websiteContext=http://localhost:8080/kadjoukor
...according to whether the app is running locally or on the cloud. I am not sure how to achieve that with the #Value("${websiteContext}") annotation...
What is the best practice for dealing with such an issue?
If you are using Spring 3.1 or later, you can take advantage of bean profiles and the CloudFoundry "cloud" profile to load a different properties file depending on the environment. That might look something like this in a Spring XML configuration file:
<beans profile="default">
<context:property-placeholder location="default.properties"/>
</beans>
<beans profile="cloud">
<context:property-placeholder location="cloud.properties"/>
</beans>
Here are a few good blog posts that explain how this works in more detail:
SPRING 3.1 M1: UNIFIED PROPERTY MANAGEMENT
USING CLOUD FOUNDRY SERVICES WITH SPRING: PART 4 – SPRING PROFILES

Combine OSGi blueprint and spring configuration

Are there any good/best practices regarding the combination of Spring configuration and OSGi Blueprint (e.g. Gemini Blueprint)? Which XML files do you use? Where do you put them in your OSGi bundles (META-INF/spring, OSGi-INF)? Which of these practices will allow you to reuse your bundles in combination with a non-Gemini-implementation of Blueprint?
Background: We are in the process of switching from Spring/Spring DM to Spring/Blueprint. I am aware of Blueprint defining a <bean> element. However we occasionally face the situation that the limited bean definition capabilities of the Blueprint specification do not meet all our needs. So it seems to be a good choice to use Spring configuration within our bundles and Blueprint for wiring bundles via OSGi services.
Which XML files do you use? Where do you put them in your OSGi bundles
(META-INF/spring, OSGi-INF)? Which of these practices will allow you
to reuse your bundles in combination with a non-Gemini-implementation
of Blueprint?
Gemini Blueprint treats both of these directories equally, but OSGI-INF/blueprint/*.xml is the only one specified in the generic OSGi Blueprint specification.
A suggested practice from the Gemini Blueprint documentation is:
[...] A
suggested practice is to split the application context configuration
into at least two files, named by convention modulename-context.xml
and modulename-osgi-context.xml. The modulename-context.xml file
contains regular bean definitions independent of any knowledge of
OSGi. The modulename-osgi-context.xml file contains the bean
definitions for importing and exporting OSGi services. It may (but is
not required to) use the Gemini Blueprint OSGi schema as the top-level
namespace instead of the Spring 'beans' namespace.
I tried this, and it works great. I use Gemini Blueprint for one of my projects which has the files META-INF/spring/context.xml, which defines my beans and their relationships, and META-INF/spring/osgi-context.xml, which defines which beans to expose as/import from OSGi services and how. context.xml looks like
<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.0.xsd">
<bean id="myOrdinarySpringBean" class="com.acme.impl.Foo"/>
</beans>
and is a regular ordinary Spring application context with no Blueprint/OSGi configuration at all. osgi-context.xml looks like
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<service id="myOsgiService" ref="myOrdinarySpringBean" interface="com.acme.Foo"/>
</blueprint>
You could, of course, use the <beans> namespace and root element here as well, but you'd have to define a xmlns:osgi and prefix the service like so: <osgi:service .../> for that to work. In my case I don't need the Gemini specific Blueprint stuff, so I'm happy with this generic Blueprint configuration. Likewise, I could use the <blueprint> namespace in context.xml as well, but this particular application is an old one being ported to OSGi, so I prefer to keep that configuration Spring specific for now.
Another application in turn has its own osgi-context.xml like
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<reference id="myOrdinarySpringBeanImportedFromOsgi" interface="com.acme.Foo" availability="mandatory"/>
</blueprint>
and at this time doesn't, but could, have its own context.xml like
<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.0.xsd">
<bean id="myOrdinaryOtherSpringBean" class="com.acme.impl.Bar">
<property name="foo" ref="myOrdinarySpringBeanImportedFromOsgi"/>
</bean>
</beans>
and couldn't really care less whether myOrdinarySpringBeanImportedFromOsgi is imported from an OSGi service or defined as a regular ordinary Spring bean in the same application context.
These META-INF/osgi-context.xml configurations could trivially be moved to OSGI-INF/blueprint/ if I want to decouple yourself from the Gemini Blueprint implementation, but for the time being I prefer to keep the two halves in the same place to avoid making a mess of the directory structure.
Blueprint files should go under OSGI-INF/blueprint/ and are named *.xml (typically blueprint.xml). This location is per the OSGi 4.2 Blueprint spec and will work with Aries or Gemini.
Spring-DM files (as you probably know) go under META-INF/spring/ and are also named *.xml (typically beans.xml)
Both files should be able to peacefully co-exist. They'll only work, though, if you have support for each container installed.
Wiring should be done via the OSGi Service Registry.
As for migration, we have stayed on Spring-DM for capabilities that we couldn't do in Blueprint. Everything else has been migrated to Blueprint.

Using OSGi service in the camel route

I am reading the book 'Camel in Action' and I am unable to work out an example (Section 4.3.4 OsgiServiceRegistry) using OSGi service in the camel route. This is my bean (exposed as OSGi service
public class HelloBean {
public String hello(String name){
System.out.println(" Invoking Hello method ");
return "Hello " + name;
}
}
This is the spring XML file that exposes the above bean as service
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://camel.apache.org/schema/spring
http://camel.apache.org/schema/spring/camel-spring.xsd
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<bean id="helloBean" class="camelinaction.testbeans.HelloBean" />
<osgi:service id="helloService" interface="camelinaction.testbeans.HelloBean" ref="helloBean" />
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start" />
<bean ref="helloService" method="hello" />
</route>
</camelContext>
</beans>
When I execute the maven goal 'camel:run', I get the following exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloService': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: required property 'bundleContext' has not been set
Please let me know how to set the bundleContext. I am using eclipse equinox as OSGi container.
camel:run just runs a thin non-OSGi runtime using the Spring Camel configs in your project. The message that you are getting is from SpringDM (the thing that instantiates the <osgi:service id="helloService"...>) not being able to locate an OSGi environment. To get this to work you need to install the code inside a supporting container - such as Karaf of Servicemix.
If you'd like to see OSGi working with Camel, check out the Servicemix Bootstraps project at https://github.com/FuseByExample/smx-bootstraps - full documentation is there around installing and tweaking the code. The bundles you'll be interested in there are smx-ponger and smx-ponger-service, which demonstrate the consumption and provision of OSGi services respectively.
I have run into situations like this in the past where I have OSGi dependent components in my camel route and I want to run/debug through an IDE like Eclipse.
If you are looking to debug while you develop, you can deploy to ServiceMix and remotely debug:
http://servicemix.apache.org/developers/remote-debugging-servicemix-in-eclipse.html
Camel 2.10 might support your scenario out of the box with OSGi blueprint:
http://camel.apache.org/camel-run-maven-goal.html
Spring OSGI extensions are fine, but as you can see it is a bit incestuous to test the service interface when you implement and declare the bean from the same spring context. You could of course have bean reference helloBean, but that defeats the purpose.
I am not sure of spring-osgi extension behavior, but at least with the very similar camel-blueprint with pojosr the same test can be with the modified helloService element.
<to uri="bean:camelinaction.testbeans.HelloBean" method="hello" />
Note the unusual fact that where bean id normally references a bean id you are now using the fully qualified interface.
Of course, this has some unfortunate limitations. It works fine if there is only one service instance implementing the desired interface, but there is no obvious way (to me) on how to apply a filter. One alternative in that case is to resort to actually using the bundleContext property of the CamelContext and using the programmatic API. But of course we would like to avoid that in favor of declarative approaches.

Could annotation based and xml based configuration be used together in spring 2.5?

I've been working on a project where controllers have been written extending Controller classes. Could I configure and use the POJO based Controllers as well (using #Controller) in the same application?
Many thanks
Thanks jamestastic and skaffman, its working all fine now :)
Below are the lines needed to to be addeded to the web configuration file to have them working together:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" ...line1
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context ...line2
http://www.springframework.org/schema/context/spring-context-2.5.xsd"> ...line3
<context:annotation-config/> ...line4
<context:component-scan base-package="myPackage"/> ...line5
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> ...line6
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> ...line7
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> ...line8
</beans>
I was too lazy to not to add line 8 in my main application.
Many thanks
Absolutely. You can mix them together as much as you choose. DispatcherServlet should recognise both old-style and new-style controllers together in the same app.
Yes you can. You'll need to include the Spring JavaConfig project library, as annotation configuration was not part of the 2.5 core.
Here is an example I wrote a while back comparing Google Guice with Spring. Toward the bottom (look for #ImportXml), I show how you can combine Spring XML configuration with annotation configuration. The configuration looks like this:
#Configuration
#ImportXml(locations = "classpath:com/earldouglas/guicespringjc/spring/config.xml")
public class XmlSpringConfiguration {
}
See the Spring Reference regarding combining XML and Annotation configuration. This is from the documentation for Spring 3, but it should still apply (with perhaps minor changes in class names and paths from the old Spring JavaConfig project).
In Spring >= 3.0 use #ImportResource annotation
#Configuration
#ImportResource({ "classpath:/path/to/spring.xml", })
public class AppConfig {
}

Resources