sitemesh and spring MVC decorator pattern problems

18,818

Solution 1

The problem is that SiteMesh uses Request.getServletPath() which in your spring mvc application will return "/spring" for everything. I found this by implementing the com.opensymphony.module.sitemesh.DecoratorMapper interface and using it in place of the normal ConfigDecoratorMapper. Then I was able to inspect the various arguments used to map decorators to requests. Unfortunately, I think this leaves you with the only option being to use the *.html suffix in the DispatcherServelet mapping or some variant thereof.

Another option would be to configure a PageDecoratorMapper and use this tag in your original undecorated page to specify which layout to use:

 <meta name="decorator" content="layoutName" /> 

Although then you void the benefits of url mappings.

Solution 2

I've had that exact problem. What's happening is that any part of the url path you specify in the web.xml gets stripped out by the web server before it gets passed to Spring, but only if you put the wildcard at the end. You have already discovered that when your url is www.myapp.com/spring/cliente/index.html, if you put this in your web.xml

<servlet-mapping>
   <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
   <url-pattern>/spring/*</url-pattern>
</servlet-mapping>

Spring will only see the part of the request path after the /spring. In that case you need to specify your RequestMapping as "/cliente/index.html".

You can also specify your servlet mapping this way.

<servlet-mapping>
   <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
   <url-pattern>*.html</url-pattern>
</servlet-mapping>

Then Spring will see the entire request path and you can specify your request mappings like this "/spring/cliente/index.html". The same goes for Sitemesh. It only sees what the web server passes through.

Solution 3

Maybe it will be useful for someone, I have got the same problem, and after research in google and stading sitemesh sources, solve the problem, by extending ConfigDecoratorMapping. Here is it:

/**
 * Created by IntelliJ IDEA.
 * User: Inf-root
 * Date: 30.06.11
 * Time: 1:00
 *
 */

public class ConfigDecoratorMapperSpringMvcSupport extends ConfigDecoratorMapper {

    private static final Logger LOG = Logger.getLogger(ConfigDecoratorMapperSpringMvcSupport.class);

    private ConfigLoader configLoader = null;

     /** Create new ConfigLoader using '/WEB-INF/decorators.xml' file. */
    public void init(Config config, Properties properties, DecoratorMapper parent) throws InstantiationException {
        LOG.debug("init()...");
        super.init(config, properties, parent);
        try {
            String fileName = properties.getProperty("config", "/WEB-INF/decorators.xml");
            configLoader = new ConfigLoader(fileName, config);
        }
        catch (Exception e) {
            throw new InstantiationException(e.toString());
        }
    }

    /** Retrieve {@link com.opensymphony.module.sitemesh.Decorator} based on 'pattern' tag. */
    public Decorator getDecorator(HttpServletRequest request, Page page) {
        LOG.debug("getDecorator()...");
        String thisPath = request.getServletPath();
        LOG.debug("\tThisPath: " + thisPath);
        String requestURI = request.getRequestURI();
        LOG.debug("\t\tGet request URI: " + requestURI);
        //TODO check indexes
        thisPath = "/springURITemplate" + requestURI.substring(request.getContextPath().length(), requestURI.length() - 1);
        LOG.debug("\t\t\tThisPath: " + thisPath);
        String name = null;
        try {
            name = configLoader.getMappedName(thisPath);
        }
        catch (ServletException e) {
            e.printStackTrace();
        }
        LOG.debug("\tResolved decorator name: " + name);
        Decorator result = getNamedDecorator(request, name);
        LOG.debug("Decorator is null ? " + (result == null));
        return result == null ? super.getDecorator(request, page) : result;
    }
}

and my decorators.xml contains something like this

<?xml version="1.0" encoding="ISO-8859-1"?>
<decorators defaultdir="/web/decorators">
    <decorator name="admin_decorator" page="admin_decorator.jsp">
        <pattern>/springURITemplate/a/administration*</pattern>
    </decorator>
</decorators>

Tested on tomcat 7 with Spring 3.0.5

Share:
18,818
atomsfat
Author by

atomsfat

Working class today tomorrow nuevo ricos.

Updated on July 26, 2022

Comments

  • atomsfat
    atomsfat almost 2 years

    I have sitemesh with spring working, this is the configuration: decorator.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <decorators defaultdir="/styles">
        <excludes>
            <pattern>/exclude.jsp</pattern>
            <pattern>/exclude/*</pattern>
        </excludes>
        <decorator page="application/themeManager/theme.jsp" name="dos">
            <pattern>/*</pattern>
        </decorator>
    </decorators>
    

    And this is my web.xml

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <web-app 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"
        version="2.4">
    
        <!-- The master configuration file for this Spring web application -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/config/web-application-config.xml
            </param-value>
        </context-param>
    
        <!-- Enables Spring Security -->
        <filter>
            <filter-name>springSecurityFilterChain</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>springSecurityFilterChain</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    
        <!-- Agregamos el filtro de sitemesh que permite interceptar todas las llamadas que necesitamos -->
        <filter>
            <filter-name>sitemesh</filter-name>
            <filter-class>com.opensymphony.sitemesh.webapp.SiteMeshFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>sitemesh</filter-name>
            <url-pattern>/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
            <dispatcher>FORWARD</dispatcher>
            <dispatcher>ERROR</dispatcher>
        </filter-mapping>
    
        <!-- Loads the Spring web application context -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- Serves static resource content from .jar files such as spring-faces.jar -->
        <servlet>
            <servlet-name>Resources Servlet</servlet-name>
            <servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
            <load-on-startup>0</load-on-startup>
        </servlet>
    
        <!-- Map all /resources requests to the Resource Servlet for handling -->
        <servlet-mapping>
            <servlet-name>Resources Servlet</servlet-name>
            <url-pattern>/resources/*</url-pattern>
        </servlet-mapping>
    
        <!-- The front controller of this Spring Web application, responsible for handling all application requests -->
        <servlet>
            <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value></param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <!-- Map all *.spring requests to the DispatcherServlet for handling -->
        <servlet-mapping>
            <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
            <url-pattern>/spring/*</url-pattern>
        </servlet-mapping>
    
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>
    
    </web-app>
    

    This work, but when I change the pattern in decorator.xml for something like

    <decorator page="application/themeManager/theme.jsp" name="dos">
        <pattern>/spring/cliente/index</pattern>
    </decorator>
    

    it doesn't work, I try a lot of combination and nothing. Then I change the mapping for the spring servlet in the web.xml like this

    Spring MVC Dispatcher Servlet *.htm

    and define a new pattern like this:

    <decorator page="application/themeManager/theme.jsp" name="dos">
        <pattern>/cliente/index.htm</pattern>
    </decorator>
    

    And it works, so is there any way to make this to work with this mapping for the spring servlet ?

    <servlet-mapping>
        <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
        <url-pattern>/spring/*</url-pattern>
    </servlet-mapping>