Friday, September 28, 2007

Spring 2.0 schemas not found? And the solution is...

We've experienced a mysterious problem that the Spring 2.0 schemas could not be found when the application context is being created inside an EJB 2.1, using Spring's AbstractStatelessSessionBean.

The problem manifests itself with the following exception:
org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 18 in XML document from class path resource [springContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
Caused by:
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'beans'.
at org.apache.xerces.util.ErrorHandlerWrapper.createSAXParseException(Unknown Source)
at org.apache.xerces.util.ErrorHandlerWrapper.error(Unknown Source)


Normally this is caused by the incorrect XML namespace or schema location declaration at the head of the application context. However, in our case, the declaration was correct:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
...
</bean>


After some research, we found that other people have also experienced this problem.

The cause is that somehow the "META-INF/spring.schemas" and "META-INF/spring.handlers" files packaged in spring.jar could not be found.

What are these two files?
  • "spring.schemas" specifies the classpaths of all Spring schemas within the spring.jar. The content of this file is listed below:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd
http\://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd
http\://www.springframework.org/schema/tx/spring-tx-2.0.xsd=org/springframework/transaction/config/spring-tx-2.0.xsd
http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd

http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-2.0.xsd
http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd
http\://www.springframework.org/schema/tx/spring-tx.xsd=org/springframework/transaction/config/spring-tx-2.0.xsd
http\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd

  • "spring.handlers" specifies the handler class that implements the NamespaceHandler interface for each namespace. The content of this file is listed below:

http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

One raised the topic in Spring's support forum that it was because of no internet access for the application server to access the schemas over the internet. Another post even provided a solution to reference the schemas using classpath prefix directly in the application context configuration files.

However, that's not the root cause and a good solution, because even if the application server can access the schemas over the internet or via classpath prefix, the bootstrap code still cannot access the "spring.handlers" file and would not know how to handle various namespaces other than the default "beans".

In fact, the root cause is that the META-INF directory of a can be blocked when referenced inside an EJB jar.

I tried a few ways to get around this problem, and the final solution I adopted is to extract these two files and put them in the META-INF directory of a JAR file that contains only this directory, and place this JAR file in the lib/ext directory of the application server domain where the application is deployed.

I hope this would help others who may face this problem.

11 comments:

Anonymous said...

Good information, actually i was facing such an issue.

Anonymous said...

Thanks a lot. We wasted lots of time for same problem.

Anonymous said...

Awesome! This is really helpful.

Anonymous said...

I think instead of doing this you can try out changing the way you access the beans xml file. Try this:

ClassPathResource res = new ClassPathResource("beans.xml");

I did not have to add any extra jar file.

Alex Wei said...

The problem is probably Sun Application Server 8.x specific. I wouldn't think SpringSource would release Spring in such a manner that it wouldn't work in any EJB 2.1 container.

nitinpai,
What we did was extending Spring's AbstractStatelessSessionBean, which will load the application context. It can share the same application context across all EJB instances and can shutdown properly when all EJB instances are destroyed. We'd rather not to load the application context inside our code...

Anonymous said...

I'm sorry to ask but y tried to do your solution. I used spring security aswell and it has its own schemas and handlers. so I put them all in one file (spring schemas and spring security schemas) and then generate the .jar file.
Then copy the jar file in two parts WEB-INF/lib and the lib/ directory of my tomcat server and it still doesn't find the schema declaration. Can you specify in which directory it should be.

thanks in advanced

Anonymous said...
This comment has been removed by a blog administrator.
Anonymous said...

We are not connected to the internet and under load this is causing a train wreck.

Patrick said...

I have the same problem in my Jboss Server and your workaround works also well for me.
But I really don't understand actually the root cause.......?

Alejandro Gomez said...
This comment has been removed by the author.
Alejandro Gomez said...

I had a similar problem, involving a servlet.

I was using AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml"); to obtain the spring context.

I tried using the code below inside the servlet's doGet

ClassPathResource res = new ClassPathResource("context.xml");
ApplicationContext ctx = (AbstractApplicationContext) WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());

and it worked! (spring context was obtained without any issue)

Perhaps your context is not the same as mine, but this approach could help...