Binary Lion Studios

I code for fun and for food.

Spring portlet MVC root context

1
java.lang.IllegalStateException: Root context attribute is not of type WebApplicationContext

This error message plagued me for several hours. I don’t think I ever found a good answer, but I did find a way to get around it.

Here’s the situation: I developed a portlet for Liferay using Spring Portlet MVC and things went fine. Then I tried to develop another one and when I went to deploy, I got the dreaded exception. I still don’t understand the root of the problem, but I learned Spring has different types of application context objects: Web, Portlet, etc… It seemed like Spring was instantiating a portlet application context when it needed a web one.

I got around the error by doing this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
  xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>ViewRendererServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ViewRendererServlet</servlet-name>
    <url-pattern>/WEB-INF/servlet/view</url-pattern>
  </servlet-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

The ContextLoaderListener will look for WEB-INF/applicationContext.xml. Define all your beans as usual in that file. Leave your portlet context file, but move all bean defs to the applicationContext.xml file. Just leave a stub in the portlet context file like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- portletname-portlet.xml -->
<?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:p="http://www.springframework.org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:util="http://www.springframework.org/schema/util"
  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 
       http://www.springframework.org/schema/context/spring-context-2.5.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-2.5.xsd">

</beans>

When you redeploy, you will see the following exception:

1
2
3
4
5
6
7
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
  at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:182)
  at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
  at com.liferay.portal.spring.context.PortletContextLoaderListener.contextInitialized(PortletContextLoaderListener.java:78)
  at com.liferay.portal.kernel.servlet.PortalClassLoaderServletContextListener.portalInit(PortalClassLoaderServletContextListener.java:90)
  at com.liferay.portal.kernel.util.PortalInitableUtil.init(PortalInitableUtil.java:48)
        ...

This exception occurs because the web context is already established when Spring goes to create the portlet context. I haven’t found a way around this, but as long as you define all your beans in applicationContext.xml, things will work normally.