Search code examples
springthymeleafspring-webflow-2

Spring MVC + Webflow 2 + Thymeleaf fragments: can't configure .HTML extension


I have a Spring MVC application with Thymeleaf configured to use fragments (NO tiles!) and all files have .html extension. Everything is working fine.

Now i'm trying to setup Webflow but, when i call my webflow url, i get 404 error as he tries to load a JSP view instead of html (outside flow, everything is fine):

HTTP Status 404 - /app/WEB-INF/views/contest/contest-step1.jsp

I know that put kilometers of line of code isn't good, but honestly i don't know which pieces are interesting and which no.

ThymeleafConfig:

@Configuration
public class ThymeleafConfig {

    @Bean
    public TemplateResolver templateResolver() {
        ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
        templateResolver.setPrefix("/WEB-INF/views/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode("HTML5");

        /**
         * only on development machine
         */
        templateResolver.setCacheable(false);

        return templateResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();

        Set<IDialect> dialects = new HashSet<IDialect>();
        dialects.add(springSecurityDialect());
        templateEngine.setAdditionalDialects(dialects);          

        templateEngine.setTemplateResolver(templateResolver());
        return templateEngine;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine());
        return resolver;
    }

    @Bean
    public SpringSecurityDialect springSecurityDialect(){
        SpringSecurityDialect dialect = new SpringSecurityDialect();
        return dialect;
    }
}

WebAppConfig:

@Configuration
@EnableWebMvc
@ComponentScan("com.myapp") 
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    }   

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
        UrlBasedViewResolver resolver = new UrlBasedViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }

    // Maps resources path to webapp/resources
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }


    /**************************************************
     * 
     * Web Flow: wizard contest
     * 
     */
    @Autowired
    private WebFlowConfig webFlowConfig;

    /**
     * Maps request paths to flows in the flowRegistry;
     * e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" 
     *
     * Configuring this mapping allows the Dispatcher to map application resource paths 
     * to flows in a flow registry. 
     * For example, accessing the resource path /hotels/booking would result in a 
     * registry query for the flow with id hotels/booking. 
     * If a flow is found with that id,
     * that flow will handle the request. If no flow is found, the next handler mapping in the
     * Dispatcher's ordered chain will be queried or a "noHandlerFound" response will be returned.
     */ 
    @Bean
    public FlowHandlerMapping flowHandlerMapping() {
        FlowHandlerMapping handlerMapping = new FlowHandlerMapping();
        handlerMapping.setOrder(-1);    //0 ?
        handlerMapping.setFlowRegistry(this.webFlowConfig.flowRegistry());
        return handlerMapping;
    }

    @Bean
    public FlowHandlerAdapter flowHandlerAdapter() {
        FlowHandlerAdapter handlerAdapter = new FlowHandlerAdapter();
        handlerAdapter.setFlowExecutor(this.webFlowConfig.flowExecutor());
        handlerAdapter.setSaveOutputToFlashScopeOnRedirect(true);
        return handlerAdapter;
    }

    @Bean(name="contest/add") //defined in in WebFlowConfig
    public ContestFlowHandler ContestFlowHandler() {
        return new ContestFlowHandler();
    }

    /* TODO
    @Bean
    public AjaxThymeleafViewResolver tilesViewResolver() {
        AjaxThymeleafViewResolver viewResolver = new AjaxThymeleafViewResolver();
        viewResolver.setViewClass(FlowAjaxThymeleafTilesView.class);
        viewResolver.setTemplateEngine(templateEngine());
        return viewResolver;
    }   */





}

WebFlowConfig:

@Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
    @Autowired
    private ThymeleafConfig thymeleafConfig;
    //private WebFlowConfig WebAppConfig;

    @Bean
    public FlowDefinitionRegistry flowRegistry() {

                return getFlowDefinitionRegistryBuilder()
                        .addFlowLocation("/WEB-INF/views/gare/gare-add-flow.xml", "gare/add")
                        .build();

    }


    @Bean
    public FlowExecutor flowExecutor() {
        return getFlowExecutorBuilder(flowRegistry())               

                .setMaxFlowExecutions(5)                    

                .setMaxFlowExecutionSnapshots(30)

                .build();
    }

    @Bean
    public FlowBuilderServices flowBuilderServices() {

        return getFlowBuilderServicesBuilder()
                .setViewFactoryCreator(mvcViewFactoryCreator())
                .build();
    }

    @Bean
    public MvcViewFactoryCreator mvcViewFactoryCreator() {

        MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
        factoryCreator.setViewResolvers(Arrays.<ViewResolver>asList(this.thymeleafConfig.thymeleafViewResolver()));
        factoryCreator.setUseSpringBeanBinding(true);
        return factoryCreator;
    }


}

I found a trick that gives me a quick solution, but honestly i would like don't do this, but correctly configure the resolver. The trick is to set the view file in flow xml file:

<view-state id="contest-step1" model="contest" view="/WEB-INF/views/contest/contest-step1.html"></view-state>

thanks


Solution

  • Try the following in flowRegistry method.

    return getFlowDefinitionRegistryBuilder(flowBuilderServices())
    

    instead of

    return getFlowDefinitionRegistryBuilder()
    

    Hope this helps.