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.
Related
My project based on Apache karaf osgi and dependency inject is done through blueprint file. I wish to know if getter and setter are really required for such beans. I tested it without getter and setter methods and it works, but not sure if that follow best practice. My motive is to just reduce LOC from that file.
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"..>
.
.
<bean id="emailServiceImpl" class="com.mycompany.EmailServiceImpl">
<property name="applicationEnvironment" value="$(staging)" />
.
.
<bean id="orderDispatcherImpl" class="com.myCompany.OrderDispatcherImpl"
ext:field-injection="true" init-method="init">
<property name="emailService" ref="emailServiceImpl"/>
The Blueprint specification defines only injection of properties via JavaBeans-style setter methods.
Field injection is an extension which is specific to the Apache Aries implementation of Blueprint as used in Karaf. Therefore it will not work in other Blueprint implementations.
If you wish your blueprint container definition to be portable across implementations then it would be better to use JavaBeans-style setter methods. If you don't care about this, then you can use field injection and forget about the setter methods.
However, note that another reason to keep the setter methods may be for unit testing purposes.
In a Java Web Project with Spring and JSF, I want do this: I want to have a service that in the first run of the app, reads a file and puts its data to a variable. then other classes can read that variable.
In fact I want that file reads one time and after that I just query the data, even web pages changed via links and navigation system.
Is there a Spring annotation to turn class to a service like this? Should I have some XML config files to specify a class as a service? I don't know what I have to do. What I know is that it can be done via Spring and I can get its data from JSF components, but how?
I have to do this based on MVC.
You can have it lazy-loaded on first call of the service. Or you can add a ContextListener to initialize the service on startup of the web context, assuming you are deploying to a J2EE container. Or you can make that service a spring bean with an init-method that initializes the data. Or any one of a hundred different ways. You'll need to determine what works best for your application.
Here's a spring example:
<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="myService"
class="org.your.Service"
init-method="initializeData"></bean>
</beans>
How you get hold of that service is up to you. You can either have the data stored statically on the class, or you can use the spring context to retrieve this single instance of the spring bean.
Spring documentation for lifecycle methods: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-lifecycle
I'm new to Spring and trying to figure out the best way to handle the following scenario:
We have an application where for local development and testing, all configuration values are pulled from a Properties file. When the app is deployed on to the App Server (Websphere in this case), instead of properties file we use JNDI resource properties.
Is there an accepted way of handling this in Spring? For a non-Spring application I probably would have done something like this using a good ol' factory pattern to decide the config source. For Spring, I've seen examples that use different context XML files per environment (sounds messy), or make use of Spring "Profiles".
Is there a generally accepted practice for this scenario?
Spring profiles are rather new and they were added precisely to address your problems. Moreover they should deprecate all other workarounds like different context XML files you mention.
For the sake of completeness here is an example:
<beans profile="test">
<context:property-placeholder location="/foo/bar/buzz.properties" />
</beans>
<beans profile="prd">
<jee:jndi-lookup id="properties" jndi-name="foo/bar/name"/>
</beans>
Depending on which profile you choose during deployment/startup, only one of the beans above will be instantiated.
Another approach I've never tried but seems to fit your case is default-value attribute in jee namespace:
<jee:jndi-lookup id="properties" jndi-name="foo/bar/name" resource-ref="true"
default-value="classpath:foo.properties"/>
Not sure if this will help you though.
Assuming Spring 3.1, try using profiles like Tomasz suggested, but instead of setting individual JNDI values for production, use
<beans profile="prd">
<context:property-placeholder/>
</beans>
In Spring 3.1, ContextLoaderListener apparently pulls in JNDI props as a PropertySource by default, so with property-placeholder, when you need to access a value you can just use ${some/jndi/name} in applicationContext.xml or a #Value annotation.
To make sure the webapp gets the values from JNDI, add
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>prd</param-value>
</context-param>
to web.xml.
In your tests, set the system property 'spring.profiles.active' to 'test', and you'll get the values from the props file.
one way to go is you use jndi also for local dev and testing. You could define the same jndi name. I don't know what's your testing server, in practice we use jetty, and maven-jetty plugin to test. It is lightweight and can run from your ide.
another way is like what you said in your question. Making use of Spring profile. Then you could declare different transactionManager beans with same id/name. of course they should be in different profiles. At runtime you could decide which profile should be activated, that is, which bean should be used.
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.
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