spring-mvcosgispring-annotationsslingspring-dm

Loading spring dm in OSGi container


I am trying to load spring dm in my OSGi bundle. I have followed a tutorial. My aim is, for a particular URL, the URL should be processed by spring instead of default sling servlet. I am partly successful in achieving the same.

Sometimes, It doesn't work. My globaldispatcher servlet is not being initialized. The issue is intermittent. Some times my spring servlet gets initialized just fine. But some times I get this error :

[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet FrameworkServlet 'globaldispatcher': initialization started
[SpringOsgiExtenderThread-2] net.jasonday.examples.sling.spring.mvc.sling.SlingDispatcherServlet Context initialization failed
java.lang.IllegalArgumentException: bundle context should be set before refreshing the application context
at org.springframework.util.Assert.notNull(Assert.java:112)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.normalRefresh(AbstractDelegatedExecutionApplicationContext.java:179)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext$NoDependenciesWaitRefreshExecutor.refresh(AbstractDelegatedExecutionApplicationContext.java:89)
at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:175)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:467)
at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:483)
at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:358)
at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:325)
at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:127)
at javax.servlet.GenericServlet.init(GenericServlet.java:158)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.createServlet(SlingServletResolver.java:988)
at org.apache.sling.servlets.resolver.internal.SlingServletResolver.bindServlet(SlingServletResolver.java:936)
at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)

Java version : 1.7.0_45

I am using following dependencies as mentioned in tutorial: (All are OSGi bundles)

Spring AOP v3.0.6.RELEASE 
Spring ASM v3.0.6.RELEASE 
Spring Aspects v3.0.6.RELEASE 
Spring Beans v3.0.6.RELEASE 
Spring Context v3.0.6.RELEASE 
Spring Context Support v3.0.6.RELEASE 
Spring Core v3.0.6.RELEASE 
Spring Expression v3.0.6.RELEASE
Spring Web v3.0.6.RELEASE 
Spring Web Servlet v3.0.6.RELEASE 
Spring OSGi IO v1.2.1 
Spring OSGi Core v1.2.1 
Spring OSGi Extender v1.2.1 
Spring OSGi Annotation v1.2.1 
Spring OSGi Web v1.2.1 
AOP Alliance v1.0.0
CGLib 2.2.0
Commons Lang v2.6 
Commons Codec v1.5 
Commons Logging v1.1.1 
ASM v3.2.0 
JSR 330 (javax.inject) v1.0.0 
JSR 250 (javax.annotation) v1.0.0

Following is the code from my spring-osgi.xml which is present in META-INF/spring/ folder

 <osgi:service ref="globalSlingDispatcherServlet">
<osgi:interfaces>
  <value>javax.servlet.Servlet</value>
</osgi:interfaces>

<osgi:service-properties>
  <entry key="sling.servlet.resourceTypes" value="examples/sling/spring/mvc/dispatcher/global" />
  <entry key="sling.servlet.extensions">
    <array>
      <value>json</value>
      <value>html</value>
    </array>
  </entry>
  <entry key="sling.servlet.methods">
    <array>
      <value>GET</value>
      <value>POST</value>
    </array>
  </entry>
  <entry key="contextClass" value="org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext"/>
  <entry key="sling.core.servletName" value="globaldispatcher" />
</osgi:service-properties>

Below is the code from META-INF\spring\spring.xml

<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc.sling" />
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />

Following is the code from WEB-INF/globaldispatcher

<context:annotation-config />
<context:component-scan base-package="net.jasonday.examples.sling.spring.mvc">
    <context:exclude-filter type="regex"
        expression="net\.jasonday\.examples\.sling\.spring\.mvc\.sling\..*" />
</context:component-scan>
<aop:aspectj-autoproxy />
<bean class="org.springframework.osgi.extensions.annotation.ServiceReferenceInjectionBeanPostProcessor" />

SlingConfiguration.java :

@Configuration
public class SlingConfiguration {
@Bean
@DependsOn("slingContextLoader")
public SlingDispatcherServlet globalSlingDispatcherServlet() {
    return new SlingDispatcherServlet();
    }
}

SlingContextLoader.java :

@Component
public class SlingContextLoader extends ContextLoader {

private ServletContext servletContext;

private ApplicationContext applicationContext;

public ApplicationContext getApplicationContext() {
    return applicationContext;
}

@Inject
public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}

public ServletContext getServletContext() {
    return servletContext;
}
@ServiceReference
public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
}
@Override
protected Class<?> determineContextClass(
        ServletContext currentServletContext) {
    return OsgiBundleXmlWebApplicationContext.class;
}
@Override
protected ApplicationContext loadParentContext(
        ServletContext currentServletContext) {
    return applicationContext;
}

@PostConstruct
public void init() {
    initWebApplicationContext(servletContext);
}
@PreDestroy
public void destroy() {
    closeWebApplicationContext(servletContext);
}
}

My SlingDispatcherServlet.java

public class SlingDispatcherServlet extends DispatcherServlet implements     Servlet {
  @PreDestroy
  @Override
  public void destroy() {
super.destroy();
String attrName = getServletContextAttributeName();
getServletContext().removeAttribute(attrName);
  }
}

Solution

  • This issue is being caused because the Spring annotations like @PostConstruct are not working as expected some times because of OSGi class loading issues.

    Hence a workaround would be to not to rely on annotations. The below code solved the issue for me.

    I am calling init() method manually in setServletContext() in SlingContextLoader

    @ServiceReference
    public void setServletContext(ServletContext servletContext) {
        log.info("in setServletContext");
        this.servletContext = servletContext;
        init();
    }
    

    in init() method I am adding following code :

    //@PostConstruct
    public void init() {
        log.info("in init");
        //Remove existing application context if any
        if (servletContext
                .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            log.info("removing existing application context to create new one");
            closeWebApplicationContext(servletContext);
        }
        initWebApplicationContext(servletContext);
    }