3

I have a Spring MVC 4.2.x project. I am configuring it via XML based configuration file:

<servlet>
    <servlet-name>foo</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

In the context.xml file I have configured resources and annotation-driven mode:

<mvc:resources mapping="/webjars/**" location="/webjars/"/>
<mvc:annotation-driven/>

Everything works fine except the one thing: webjars-locator - Webjars locator doesn't work at all.


I've started look into the Spring MVC sources to understand what's going wrong, and found, that webjars-locator works through WebJarsResourceResolver class, which's object is added in the ResourceChainRegistration.getResourceResolvers() class method.

The full sequence looks like:

WebMvcConfigurationSupport.resourceHandlerMapping() >> ResourceHandlerRegistration.getHandlerMapping() >> ResourceHandlerRegistration.getRequestHandler() >> ResourceChainRegistration.getResourceResolvers(), where it is added as:

if (isWebJarsAssetLocatorPresent) {
    result.add(new WebJarsResourceResolver());
}

The problem, is that in case of XML based configuration described above, this sequence is not invoked, neither WebMvcConfigurationSupport class is used.

Furthermore, if I add

@EnableWebMvc
@Configuration
public class WebConfig {
}

into project, WebMvcConfigurationSupport obviously works, but in ResourceHandlerRegistration.getHandlerMapping():

protected AbstractHandlerMapping getHandlerMapping() {
    if (registrations.isEmpty()) {
        return null;
    }
...
}

registrations is empty!


After all, the only one way how to forcibly make Spring to add WebJarsResourceResolver into the resolver chain, is:

1) remove <mvc:resources mapping="/webjars/**" location="/webjars/"/> from the context.xml and

2) add addResourceHandlers into WebConfig:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
            .addResourceHandler("/webjars/**")
            .addResourceLocations("/webjars/")
            .setCachePeriod(3600)
            .resourceChain(true)  // !!! very important
    ;
}

My question is: what am I doing wrong? Why XML bases configuration doesn't cause WebJarsResourceResolver to be registered?

Andremoniy
  • 34,031
  • 20
  • 135
  • 241

2 Answers2

3

Let's say that the context root path is /ctx. With your configuration, a resource path as /webjars/RESOURCE is actually mapped to /ctx/webjars/RESOURCE; in contrary to common expectation that it should be mapped to /webjars/RESOURCE.

Based on Spring 4.2.x documentation and an example similar issue, you need to map /webjars/** to the default dispatcher servlet as:

This allows for mapping the DispatcherServlet to "/" (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures a DefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priority relative to other URL mappings.

which means adding the following should fix the issue:

<mvc:resources mapping="/webjars/**" location="/webjars/">
    <mvc:resource-chain>
        <mvc:resource-cache />
    </mvc:resource-chain>
</mvc:resources>
<mvc:annotation-driven />
<mvc:default-servlet-handler />

Another note that is the example petclinic uses location="classpath:/META-INF/resources/webjars/" for webjars which I am not sure is relevant here.

Hope this helps.


Added by @Andremoniy:

for Spring 4 it has slightly different markup:

<mvc:resources mapping="/webjars/**" location="/webjars/">
    <mvc:resource-chain resource-cache="true"/>
</mvc:resources>
Andremoniy
  • 34,031
  • 20
  • 135
  • 241
nobeh
  • 9,784
  • 10
  • 49
  • 66
  • 1
    Ha! So the problem is only with ``, I didn't know this obvious way for specifying it in XML, thank you! This still doesn't answer the question why it isn't described in webjar documentation, but I accept it as it solves the issue in the desired way! – Andremoniy May 26 '17 at 15:17
0

To answer your remaining question "why it isn't described in webjar documentation":

It is/was an issue that has been reported on May 3, 2016:

https://github.com/webjars/webjars/issues/1395

As described on this page this issue also mentions that Spring MVC requires the resourceChain() method. I created a PR today to fix the issue:

https://github.com/webjars/webjars/pull/1946

FYI: I tried to get version agnostic url's working the way it was described in the documentation but didn't succeed. Along the way I also found the following issue that mentions that "The resourceChain() method must be called for version-agnostic WebJars":

https://github.com/spring-projects/spring-framework/issues/25410