Spring Boot with Apache Tiles
Asked Answered
L

5

9

I am attempting to migrate my java application to Spring Boot. Currently, I am running Spring MVC 3.2 with Apache Tiles. When I migrated to Spring Boot, my controllers still get called fine, they pass the view to the view Resolver, but when Tiles goes to pull the JSP file in, things fall apart. The error message I get is:

13:48:46,387 TRACE org.springframework.web.servlet.handler.SimpleUrlHandlerMapping:127 - No handler mapping found for [/jsp/layout/layout.jsp]

Has anyone successfully used Apache Tiles with Spring Boot? Any ideas how to do it?

Thanks in advance for any ideas!


More Details:

@Bean
    public UrlBasedViewResolver viewResolver(){
        LOGGER.trace("Entering tiles configurer");
        UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
        viewResolver.setViewClass(TilesView.class);
        return viewResolver;
    }
    @Bean
    public TilesConfigurer tilesConfigurer(){
        LOGGER.trace("Entering tiles configurer");
        System.out.println("Entering tiles configurer");
        TilesConfigurer tilesConfigurer = new TilesConfigurer();
        String[] defs = {"/WEB-INF/tiles-defs.xml"};
        tilesConfigurer.setDefinitions(defs);
        return tilesConfigurer;
    }

controller:

        @RequestMapping(value="/")
        public ModelAndView index(ModelAndView mav, HttpServletRequest request, HttpServletResponse resp,RedirectAttributes ra){
            LOGGER.trace("Arrived in Home Controller");
            mav.addObject("profile",appContext.getEnvironment().getActiveProfiles()[0]);
            mav.setViewName("home");
            return mav;
        }

tiles-definitions :

        <definition name="layout" template = "/jsp/layout/layout.jsp">
        </definition>
        <definition name="home" extends="layout">
            <put-attribute name="body" value = "/jsp/home.jsp" />
        </definition>
Leprechaun answered 5/4, 2014 at 18:56 Comment(7)
Is your app a WAR (i.e. does it even have a WEB-INF)? Maybe try setting the order on your ViewResolver to something lower than the default (so there's no chance of a clash)? P.S. (not relevant to getting Tiles to work) you don't use the HttpServlet* parameters in your controller, and it's bad style to require them, so you can just remove them.Vaticinal
I am attempting to use the JAR based app created by Spring Boot that uses an embedded Tomcat server. And thanks for the tip on the HttpServlet parameters. I tried specifying @Order(value=Ordered.LOWEST_PRECEDENCE-100) on the view resolver...same issue still existsLeprechaun
If you have a JAR then you shouldn't have any "src/main/webapp" (or if you do it won't be included in the archive). Try putting everything in "src/main/resources" (and probably it makes no sense then to use "WEB-INF"). I don't know if Tiles knows how to load resources from the classpath, so that might be your biggest challenge.Vaticinal
Hmm...I tried putting my jsp's in the src/main/resources, but it didn't find them. What is interesting is that tiles is able to successfully read the tiles-def.xml that is in src/main/webapp/WEB-INF. The problem occurs when Tiles goes to get the jsp's.Leprechaun
@Leprechaun Have you got a solution to this? I'm experienced the same problem with a similar layout.Somnolent
@FirdousAmir I have not got a solution...I gave up trying after it seemed that jsp's and jar's don't play together.Leprechaun
I got it solved by adding tomcat-embed-Jasper and using tiles 3.0.4.Somnolent
W
7

I also ran into a similar issue, and was able to resolve it with help from various answers above. To help others who might encounter this issue in the future, I created a simple Maven project at https://github.com/barryku/spring-boot-tiles to include minimum settings needed for using tiles with Spring Boot. The following are a few things to pay attention to,

  1. tomcat-embed-jasper is needed for rendering JSP pages
  2. remember to use org.springframework.web.servlet.view.tiles3 not tiles2 package
  3. jstl is used by Spring's tiles3 integration, so it must be included

I added required files step by step, so you can take a look at the commit history at https://github.com/barryku/spring-boot-tiles/commits/master to get better understanding what were added in each step.

Welker answered 19/8, 2015 at 2:6 Comment(0)
E
4

I have my main layouts in /src/main/webapp/WEB-INF/layouts/layouts.xml with sample entries like:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">

<tiles-definitions>

  <definition name="default" template="/WEB-INF/layouts/default.jspx">

    <put-attribute name="menu" value="/WEB-INF/views/menu.jspx"/>
    <put-attribute name="sidebar" value="/WEB-INF/views/sidebar.jspx"/>    
    <put-attribute name="header" value="/WEB-INF/views/header.jspx" />
    <put-attribute name="footer" value="/WEB-INF/views/footer.jspx" />
  </definition>

  <definition name="public" template="/WEB-INF/layouts/default.jspx"> 

    <put-attribute name="menu" value="/WEB-INF/views/menu.jspx"/>
    <put-attribute name="sidebar" value="/WEB-INF/views/sidebar.jspx"/> 
    <put-attribute name="header" value="/WEB-INF/views/header.jspx" />
    <put-attribute name="footer" value="/WEB-INF/views/footer.jspx" />
  </definition>

</tiles-definitions>

And then more specific tiles definitions in /src/main/webapp/WEB-INF/views/views.xml with sample entries like:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN" "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>

  <definition extends="default" name="index"> 
    <put-attribute name="body" value="/WEB-INF/views/index.jspx"/>
  </definition>

  <definition extends="public" name="login">
        <put-attribute name="sidebar" value=""></put-attribute>
        <put-attribute name="body" value="/WEB-INF/views/login.jspx"/>
    </definition>
</tiles-definitions>

Then you need your configuration class:

@EnableWebMvc
@Configuration
public class RootMvcConfiguration extends WebMvcConfigurerAdapter {
    ...
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

      registry.addViewController("/").setViewName("index");
      registry.addViewController("/login").setViewName("login");
    }
    ...
}

Beans in that class that relate to tiles depend - if you want to use JSPs or Thymeleaf.

For JSP, you should have there:

 @Bean 
 public UrlBasedViewResolver tilesViewResolver(){

    UrlBasedViewResolver tilesViewResolver = new UrlBasedViewResolver();
    tilesViewResolver.setViewClass(TilesView.class);
    return tilesViewResolver;
}
 @Bean
 public TilesConfigurer tilesConfigurer(){ 

    String[] definitions = new String[] {
            "/WEB-INF/layouts/layouts.xml",
            "/WEB-INF/views/**/views.xml" /*Scans directories for Tiles configurations */
            };

    TilesConfigurer tilesConfigurer = new TilesConfigurer();
    tilesConfigurer.setDefinitions(definitions);
    return tilesConfigurer;

 }

If you use Thymeleaf you should have something like that:

@Bean
public MessageSource messageSource(){

    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
    messageSource.setBasenames("classpath:META-INF/i18n/messages");
    messageSource.setFallbackToSystemLocale(false);
    return messageSource;
}
@Bean 
public ServletContextTemplateResolver templateResolver() {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
        resolver.setPrefix("/WEB-INF/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setOrder(2);
        resolver.setCacheable(false);            
        return resolver;
}

@Bean 
public SpringTemplateEngine templateEngine() throws Exception {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver());
        engine.setTemplateEngineMessageSource(messageSource());
        engine.addDialect(new TilesDialect());            
        engine.addDialect(new SpringSecurityDialect());            
        engine.afterPropertiesSet();
        return engine;
}
@Bean
public TilesConfigurer tilesConfigurer(){ 

    String[] definitions = new String[] {
            "/WEB-INF/layouts/layouts.xml",
            "/WEB-INF/views/**/views.xml" /*Scans directories for Tiles configurations */
            };

    ThymeleafTilesConfigurer tilesConfigurer = new ThymeleafTilesConfigurer();
    tilesConfigurer.setDefinitions(definitions);        
    return tilesConfigurer;

}

Then you can extend your views.xml and point view names to your jsp or html files and an example controller can be as follows:

@RequestMapping("/roles")
@Controller
public class RoleController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String show(@PathVariable("id") Integer id, Model uiModel) {
        uiModel.addAttribute("role", Role.findRole(id));
        uiModel.addAttribute("itemId", id);
        return "roles/show";
    }
}

If you use Maven, here are some sample lib versions:

<properties>        
    <thymeleaf.version>2.0.19</thymeleaf.version>
    <thymeleaf.extras.version>2.0.0</thymeleaf.extras.version>         
    <thymeleaf.extras.security.version>2.0.0</thymeleaf.extras.security.version>
    <tiles.version>2.2.2</tiles.version>
</properties>
...
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>${jstl.version}</version>
    </dependency>

            <dependency>
        <groupId>org.apache.tiles</groupId>
        <artifactId>tiles-core</artifactId>
        <version>${tiles.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.tiles</groupId>
        <artifactId>tiles-jsp</artifactId>
        <version>${tiles.version}</version>
    </dependency>  

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf</artifactId>
        <version>${thymeleaf.version}</version>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring3</artifactId>
        <version>${thymeleaf.version}</version>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity3</artifactId>
        <version>${thymeleaf.extras.security.version}</version>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-tiles2</artifactId>
        <version>${thymeleaf.extras.version}</version>
    </dependency>
Estafette answered 10/6, 2014 at 13:54 Comment(1)
This works perfect, simple but complete answer. Thanks!Higdon
L
1

You're going to have to package your app as a war. Spring-boot, jsp and jar packaging don't get along very well. Also makes sure you have the following in your pom.xml:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
    </dependency>
Licht answered 19/6, 2014 at 22:30 Comment(0)
R
0

Your definitions should be:

    <definition name="layout" template = "/WEB-INF/jsp/layout/layout.jsp">
    </definition>
    <definition name="home" extends="layout">
        <put-attribute name="body" value = "/WEB-INF/jsp/home.jsp" />
    </definition>
Ron answered 5/4, 2014 at 21:1 Comment(5)
I used to have my jsp folder directly under src/main/webapp...I tried moving it into the WEB-INF folder and changing the definitions per your recommendation. Unfortunately I still get the same error.Leprechaun
I edited my answer to make the reference as /WEB-INF/... instead of /WEBINF/ **typoRon
Where is your tiles-def.xml located? What are its contents?Ron
My tiles-def.xml is located at src/main/webapp/WEB-INF. I have quite a few few definitions. Do you want me to past the entire file up? It is 221 lines long.Leprechaun
Quesions: 1) How is your controller defined? 2)What context files you have? Show them.Ron
P
0

I had the same issues and other like in this question. The problem was paths ans prefixes. In Tiles definitions i had paths like /WEB-INF/views/test.html and that was a mistake. Below i'm putting full working configuration for Spring application (Thymeleaf 2, Tiles 2) and example files. Maybe it will help someone.

Beans

@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/test").setViewName("tiles/test");
    }

    @Bean
    public ServletContextTemplateResolver servletContextTemplateResolver() {
        ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setCharacterEncoding("UTF-8");     
        return resolver;
    }   

    /**
     * Tiles view resolver
     * @param templateEngine
     * @return
     */
    @Bean
    public ViewResolver vr(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver th = new ThymeleafViewResolver();
        th.setTemplateEngine(templateEngine);
        th.setViewClass(ThymeleafTilesView.class);
        th.setCharacterEncoding("UTF-8");
        th.setOrder(Ordered.LOWEST_PRECEDENCE);
        return th;
    }

    /**
     * Basic view resolver
     * @param templateEngine
     * @return
     */
    @Bean
    public ViewResolver thymeleafViewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver vr = new ThymeleafViewResolver();
        vr.setTemplateEngine(templateEngine);
        vr.setCharacterEncoding("UTF-8");
        vr.setOrder(Ordered.HIGHEST_PRECEDENCE);
        // all tiles/* views will not be handled by this resolver;
        vr.setExcludedViewNames(new String[]{"tiles/*"});
        return vr;
    }   

    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);
        log.info(templateResolver);
        templateEngine.addDialect(new SpringSecurityDialect());
        templateEngine.addDialect(new TilesDialect());
        return templateEngine;
    }

    @Bean
    @SuppressWarnings("deprecation")
    public ThymeleafTilesConfigurer tilesConfigurer() {
        ThymeleafTilesConfigurer ttc = new ThymeleafTilesConfigurer();
        ttc.setDefinitions(new String[]{"/WEB-INF/tiles.xml"});
        return ttc;
    }   
}

Tiles definitions - src/main/webapp/WEB-INF/tiles.xml

Template paths are relative to templateResolver prefix and without suffix. When i use full paths it doesn't work and got exception like

Error resolving template "/WEB-INF/views/layout.html", template might not exist or might not be accessible by any of the configured Template Resolvers

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
          "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
          "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>

  <definition name="baseLayout" template="layout">
  </definition>

  <definition name="tiles/test" extends="baseLayout">
    <put-attribute name="body" value="test"/>
    <put-attribute name="title" value="Test Page"/>
  </definition>
</tiles-definitions>

src/main/webapp/WEB-INF/views/layout.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:tiles="http://tiles.apache.org/tags-tiles">
<head>
    <title tiles:string="title">blah blah blah</title>
    <style type="text/css"></style>
</head>
<body>
    <div class="container">
        <div tiles:replace="body"></div>
    </div>
</body>
</html>

src/main/webapp/WEB-INF/views/test.html

2+2=<span xmlns:th="http://www.thymeleaf.org" th:text="2+2"></span>
Primer answered 6/12, 2016 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.