Search This Blog

Loading...

Tuesday, January 12, 2010

Spring xsd loading algorithm

People often complain that spring tries to open network connection during application context initialization in order to load '*.xsd' file(s). That shouldn't occur under normal circumstances, however, that is a case especially during application server participation. I'm going to briefly describe low-level details of '*.xsd' loading algorithm used by spring.





'*.xsd' is necessary for describing the rules of spring xml context construction. E.g. the following simple spring xml config snippet defines three spring schemas - 'beans', 'aop' and 'context':



<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
>

<context:component-scan base-package="com.spring.example.aop"/>

<bean id="methodInterceptor" class="com.spring.example.aop.AopMethodInterceptor"/>
<bean id="pojoAspect" class="com.spring.example.aop.PojoAspect"/>

<aop:config>
<aop:advisor advice-ref="methodInterceptor" pointcut="execution(* com.spring.example.aop.AopService.*(..))"/>
</aop:config>

<aop:config>
<aop:aspect ref="pojoAspect">
<aop:before method="pojoAdvice" pointcut="execution(* com.spring.example.aop.AopService.*(..))"/>
</aop:aspect>
</aop:config>

</beans>


Let's see what happens when we instantiate spring context with such config:
  1. Spring shoud parse the '*.xml';

  2. Spring defines custom EntityResolver for the xml parser - DelegatingEntityResolver;

  3. 'DelegatingEntityResolver.resolveEntity()' calls PluggableSchemaResolver.resolveEntity();

  4. 'PluggableSchemaResolver.resolveEntity()' calls 'PluggableSchemaResolver.getSchemaMappings()';

  5. 'PluggableSchemaResolver.getSchemaMappings()' tries to load properties from all resources named 'META-INF/spring.schemas' ('PluggableSchemaResolver.DEFAULT_SCHEMA_MAPPINGS_LOCATION') found at classpath during calling to PropertiesLoaderUtils.loadAllProperties();

  6. 'PluggableSchemaResolver.resolveEntity()' tries to resolve target shema against classpath using the data from loaded properties;

  7. And here is a 'PropertiesLoaderUtils.loadAllProperties()' code:

    public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
    Assert.notNull(resourceName, "Resource name must not be null");
    ClassLoader clToUse = classLoader;
    if (clToUse == null) {
    clToUse = ClassUtils.getDefaultClassLoader();
    }
    Properties properties = new Properties();
    Enumeration urls = clToUse.getResources(resourceName);
    while (urls.hasMoreElements()) {
    URL url = (URL) urls.nextElement();
    InputStream is = null;
    try {
    URLConnection con = url.openConnection();
    con.setUseCaches(false);
    is = con.getInputStream();
    properties.load(is);
    }
    finally {
    if (is != null) {
    is.close();
    }
    }
    }
    return properties;
    }




Let's check the processing one more time on 'beans' schema example:

  1. Our xml config defines 'beans' schema location as 'http://www.springframework.org/schema/beans/spring-beans-3.0.xsd';

  2. All contents of classpath resources named 'META-INF/spring.schemas' are loaded;

  3. There is 'META-INF/spring.schemas' file at 'spring-beans.jar', it contains property 'http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd' among the others;

  4. 'PluggableSchemaResolver' tries to load schema data from classpath resource defines as a target property value ('org/springframework/beans/factory/xml/spring-beans-3.0.xsd');

  5. You can see that there is 'org/springframework/beans/factory/xml/spring-beans-3.0.xsd' resource inside 'spring-beans.jar'. It's loaded and returned as a resolved xml entity;


That's it - target '*.xsd' file is loaded from classpath. Spring tries to load it from the web if it can't be loaded from classpath.
Note: spring uses the same technique for loading namespace handlers necessary for correct context instantiation - see DefaultNamespaceHandlerResolver. That's the reason why you should define custom schema definition and handler at 'META-INF/spring.schemas' and 'META-INF/spring.handlers' files as described at the reference.

4 comments:

  1. Very helpful article. Thanks.

    ReplyDelete
  2. Thanks for the article. This helped me debug a library I was using which was having a problem with schema location resolution.

    ReplyDelete
  3. Welcome :)
    I was at the same situation - schema location resolution was incorrect, hence, I spent time to checking spring processing on that and just shared the knowledge

    ReplyDelete