With some effort, I got it to work. ContextLoaderListener
is still the way to go, but it needs a few tweaks to make it OSGi aware.
The solution in my case consisted of several steps:
Step 1: Replace Gemini Blueprint with Spring OSGi
We need to make the ContextLoaderListener
create an OsgiBundleXmlWebApplicationContext
instead of a plain XmlWebApplicationContext
. As far as I know there isn't yet any distribution of Gemini Blueprint providing this class, so we will need to use Spring OSGi instead which has distributed a spring-osgi-web bundle.
Instead of using the Gemini Blueprint extender jars, I used the following Spring OSGi jars:
spring-osgi-core-1.2.1.jar
spring-osgi-extender-1.2.1.jar
spring-osgi-io-1.2.1.jar
spring-osgi-web-1.2.1.jar
(and of course their dependencies, omitted for brevity)
Since I had used the flashy new <blueprint>
root element and namespace in some of my application context configurations, that needed to be exchanged for the Spring OSGi equivalents (notice in particular how availability="mandatory"
now becomes cardinality="1..1"
):
<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-2.0.xsd
http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
<reference id="wfEngine" interface="se.sunstone.workflow.WorkflowEngine" cardinality="1..1"/>
</beans>
In Gemini Blueprint the namespaces are interchangeable, but Spring OSGi predates Blueprint and therefore the Blueprint namespaces etc. don't work in Spring OSGi.
Step 2: Prevent Spring OSGi from recognizing the war as a Spring bundle
This was achieved by simply moving my application contexts out of META-INF/spring
and instead placing them in WEB-INF/applicationContext.xml
, which is the default location for the ContextLoaderListener to look for the application context configuration.
Step 3: Make the ContextLoaderListener OSGi aware
Next, I followed these instructions to configure the ContextLoaderListener to use org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext
as the type for the application context. This then greeted me with a new error:
java.lang.IllegalArgumentException: bundle context should be set before refreshing the application context
After some searching I happened upon this blog post and tried that out. The OsgiWebBundleContext
supplied there didn't work for me, I still got the same error. By adding some trace output in this new context type, I could confirm that the bundle context in fact didn't exist:
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) Attributes in servletContext:
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.JSP_PROPERTY_GROUPS
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) javax.servlet.context.tempdir
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.JSP_TAG_LIBRARIES
17:15:20,233 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) javax.websocket.server.ServerContainer
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.jasper.SERVLET_VERSION
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.jboss.as.jsf.FACES_ANNOTATIONS
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) org.apache.tomcat.InstanceManager
17:15:20,234 FINEST [se.sunstone.workflow.web.osgi.workaround.OsgiBundleXmlWebApplicationContextWithCorrectBundleContextLookupName] (MSC service thread 1-5) End of attributes in servletContext
But at least Spring has now been made OSGi aware, which is a start.
Step 4: Work around the missing BundleContext
It would seem that either the BundleContext is never set on the ServletContext, or Spring attempts to access it before it is set. Either way, this answer inspired me to modify the workaround class from the blog post to use FrameworkUtil.getBundle(ClassFromBundle).getBundleContext()
to find the BundleContext:
public class OsgiBundleXmlWebApplicationContextSettingBundleContextFromFrameworkUtil
extends OsgiBundleXmlWebApplicationContext {
@Override
public void setServletContext(ServletContext servletContext) {
if(getBundleContext() == null) {
BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
if(context != null) {
setBundleContext(context);
}
}
// to call "this.servletContext = servletContext;" in super
super.setServletContext(servletContext);
}
}
And that worked for me! It may not be the prettiest solution, but it's the only one so far I've gotten to work.*
(Well, I also got it to work by using a BundleActivator
, but that probably wasn't any prettier.)