Thymeleaf and JSP resolvers in one Spring MVC app - spring

I have a Spring MVC application with Thymeleaf view resolver. Here is my config class:
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public SpringResourceTemplateResolver springResourceTemplateResolver() {
return new SpringResourceTemplateResolver();
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
return engine;
}
private ITemplateResolver templateResolver() {
springResourceTemplateResolver().setPrefix("/WEB-INF/templates/");
springResourceTemplateResolver().setTemplateMode(TemplateMode.HTML);
return springResourceTemplateResolver();
}
}
Now I need to add a jsp resolver to the same application. I'm adding InternalResourceViewResolver bean but it doesn't work as expected:
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
Instead of getting test_jsp page when I go to /test/jsp I get an error:
#RequestMapping(value = "/test/leaf", method = RequestMethod.GET)
public String mainPage() {
return "test_html.html";
}
#RequestMapping(value = "/test/jsp", method = RequestMethod.GET)
public String mainPage2() {
return "test_jsp";
}
Request processing failed; nested exception is
org.thymeleaf.exceptions.TemplateInputException: An error happened
during template parsing (template: "ServletContext resource
[/WEB-INF/templates/test_jsp]")
Does anybody know how to add jsp view resolver when ThymeLeaf is already in place? I need Spring to look for Thymeleaf first and when page is not found then he searches jsp. Thanks

Set the order property on the ThymeleafViewResolver to a lower integer than other order properties on other resolvers. Zero makes sense for your case.
For your controller, you'll want to return just the page name and leave off the extension (.html).
Finally, post the full trace when you're posting error messages to SO. As it stands, there could also be an issue with your JSP but a reader here can't readily tell.

package com.mmm.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
#Configuration
#EnableWebMvc
#EnableAspectJAutoProxy
#ComponentScan(basePackages = "com.mmm")
public class MainConfig implements WebMvcConfigurer {
#Autowired
WebApplicationContext webApplicationContext;
#Bean
public SpringResourceTemplateResolver getTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new
SpringResourceTemplateResolver();
templateResolver.setApplicationContext(webApplicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(getTemplateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
#Bean
ThymeleafViewResolver configureViewResolvers() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setOrder(1);
resolver.setViewNames(new String[] {"*.html"});
return resolver;
}
#Bean
public UrlBasedViewResolver getUrlBasedViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix("");
viewResolver.setOrder(2);
return viewResolver;
}

Related

Spring MVC controllers configure for thymeleaf views

I've tried to configure controllers for thymeleaf views resolver, but it doesn't work. I made controller test and they passed so i think it is servlets configuration problem.
My WebConfig looks like this:
#Configuration
#EnableWebMvc
#ComponentScan("springmvccommerce.web")
public class WebConfig implements WebMvcConfigurer{
#Bean
ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(true);
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Edit:
HomeController:
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
return "home";
}
}
And productController:
#Controller
public class ProductController {
#Autowired
private ProductRepository productRepository;
#GetMapping("/products")
public String products(Model model) {
model.addAttribute("productList", productRepository.findProduct(Long.MAX_VALUE, 20));
return "products";
}
}
If it is not enough, I've added link to repo in comment.
It seems spring boot haven't support for tomcat 10 due to the jakarta namespace, switch to tomcat 9 and retry.
Here are some links related:
https://github.com/spring-projects/spring-framework/issues/25354
Deploying Spring 5.x on Tomcat 10.x with jakarta.* package

Spring MVC Thymleaf configuration file

I am trying to implement
https://mkyong.com/spring-boot/spring-boot-hello-world-example-thymeleaf/
with a Spring MVC configuration file for greater flexibility which is actually missing in tutorial .
I took help from the thymeleaf documentation
https://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#views-and-view-resolvers-in-spring-mvc
to write the Spring MVC configuration file . Here is the code
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan
public class SpringWebConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private ApplicationContext applicationContext;
public SpringWebConfig() {
super();
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
#Bean
#Description("Thymeleaf View Resolver")
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
#Bean
#Description("Spring Message Resolver")
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Bean
#Description("Thymeleaf Template Engine")
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setTemplateEngineMessageSource(messageSource());
return templateEngine;
}
#Bean
#Description("Thymeleaf Template Resolver")
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
}
I am getting two warnings and the view is not getting displayed properly
2020-04-11 19:37:12.239 WARN 27247 --- [nio-8080-exec-2] o.s.web.servlet.PageNotFound : No mapping for GET /webjars/bootstrap/4.2.1/css/bootstrap.min.css
2020-04-11 19:37:12.241 WARN 27247 --- [nio-8080-exec-4] o.s.web.servlet.PageNotFound : No mapping for GET /webjars/bootstrap/4.2.1/js/bootstrap.min.js
how to provide mapping for the webjars/bootstrap ...
can anyone please help me out ?

After adding #PathVariable, resources become unreachable

#GetMapping("/instructor-edit-course")
private String instructorAddCourse(Model model) {
model.addAttribute("course", new Course());
return "instructor-edit-course";
}
Above code is ok. Resources are found.
But when I add #PathVariable to the path like below code, application searches resources in
http://localhost:8080/instructor-edit-course/assets/images/icon/footer/facebook-square#2x.png
instead of
http://localhost:8080/assets/images/icon/footer/facebook-square#2x.png
#GetMapping("/instructor-edit-course/{id}")
private String instructorEditCourse(#PathVariable String id, Model model) {
// go to course page
Course course = courseService.findById(Integer.parseInt(id));
model.addAttribute("course", course);
return "instructor-edit-course";
}
WebConfig class
package com.hrglob.elearning.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.xxxx.*"})
public class WebConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**")
.addResourceLocations("/public", "classpath:/static/", "classpath:/static/assets/")
.setCachePeriod(31556926);
}
#Bean
#Description("Thymeleaf template resolver serving HTML 5")
public ClassLoaderTemplateResolver templateResolver() {
var templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setOrder(0);
return templateResolver;
}
#Bean
#Description("Thymeleaf template engine with Spring integration")
public SpringTemplateEngine templateEngine() {
var templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
#Description("Thymeleaf view resolver")
public ViewResolver viewResolver() {
var viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
}

How to provide a parameter value for an autowired variable?

I want to autowire a variable :
package com.ambre.hib.dao;
public interface LangDAO {
public String _getText();
}
package com.ambre.hib.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
public class LangDAOImpl implements LangDAO {
#Autowired
private Environment env;
private String code;
public LangDAOImpl() {
}
public LangDAOImpl(String code) {
this.code = code;
}
#Override
public String _getText() {
return env.getProperty(code);
}
}
package com.ambre.hib.config;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.ambre.hib.dao.LangDAO;
import com.ambre.hib.dao.LangDAOImpl;
import com.ambre.hib.dao.UserDAO;
import com.ambre.hib.dao.UserDAOImpl;
import com.ambre.hib.model.User;
#Configuration
#ComponentScan("com.ambre.hib")
#EnableTransactionManagement
#PropertySource("classpath:lang.properties")
public class ApplicationContextConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer properties() { // managing properties file ( languages )
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name = "viewResolver")
public InternalResourceViewResolver getViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean(name = "dataSource")
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:#localhost:1521:xe");
dataSource.setUsername("system");
dataSource.setPassword("a");
return dataSource;
}
#Autowired
#Bean(name = "sessionFactory")
public SessionFactory getSessionFactory(DataSource dataSource) {
LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
sessionBuilder.addAnnotatedClasses(User.class);
return sessionBuilder.buildSessionFactory();
}
#Autowired
#Bean(name = "transactionManager")
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager transactionManager = new HibernateTransactionManager(sessionFactory);
return transactionManager;
}
#Autowired
#Bean(name = "userDao")
public UserDAO getUserDao(SessionFactory sessionFactory) {
return new UserDAOImpl(sessionFactory);
}
#Autowired
#Bean(name = "langDAO")
public LangDAO getLangDAO(String code) {
return new LangDAOImpl(code);
}
}
package com.ambre.hib.controller;
... // imports
#Controller
public class HomeController {
#Autowired
private LangDAO langDAO; // how to set here the String "title.home" to the constructor ?
#RequestMapping("/")
public String handleRequest(Model model) throws Exception {
model.addAttribute("titre", langDAO._getText());
return "UserList";
}
}
As you can see I autowired the class LangDAO in the controller. But I want to pass a String as its constructor parameter. How to do that ?
Since I can see that you've already configured the PropertySourcesPlaceholderConfigurer then you can autowire the property from the config file directly to the constructor like so:
#Autowired
public LangDAOImpl(#Value("${you_property_key}") String code) {
this.code = code;
}
or directly on the property
#Value("${you_property_key}") String code
Alternatively, if you define a #PostConstruct method you can get the property from the environment and set it yourself manually.
Values can be autowired in several ways
Spring provides a feature for Autowiring of the system properties.
#Value("${pool.size}")
public String size;
Using spring application context for passing values
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<constructor-arg value="${property.from.file}"/>
</bean>
Properties will be loaded into memory using
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:service.properties</value>
</property>
</bean>
ref: http://www.tutorialspoint.com/spring/spring_autowiring_byconstructor.htm

How to get Thymeleaf to process html in the static folder?

I'm having a hard time figuring out how to get index.html to be process by Thymeleaf in:
src/main/resources/static
Thymeleaf works perfectly in the templates directory with no configuration:
src/main/resources/templates
The environment is Spring Boot with Maven. It uses Yeoman to transpile the index.html, CSS and JS. and I need all those files to be in the same directory tree.
IDK if this is enough info, but below is my ThymeleafConfig:
package org.jeremu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import java.util.HashSet;
import java.util.Set;
#Configuration
public class ThymeleafConfig {
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
Set<ITemplateResolver> resolvers = new HashSet<ITemplateResolver>();
resolvers.add(defaultWebTemplateResolver());
resolvers.add(emailTemplateResolver());
templateEngine.setTemplateResolvers(resolvers);
return templateEngine;
}
#Bean
public ClassLoaderTemplateResolver defaultWebTemplateResolver(){
ClassLoaderTemplateResolver webTemplateResolver = new ClassLoaderTemplateResolver();
webTemplateResolver.setPrefix("static/");
webTemplateResolver.setSuffix(".html");
webTemplateResolver.setTemplateMode("HTML5");
webTemplateResolver.setCharacterEncoding("UTF-8");
webTemplateResolver.setOrder(2);
return webTemplateResolver;
}
#Bean
public ClassLoaderTemplateResolver emailTemplateResolver(){
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("templates/email/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML5");
emailTemplateResolver.setCharacterEncoding("UTF-8");
emailTemplateResolver.setOrder(1);
return emailTemplateResolver;
}
#Bean
ViewResolver viewResolver(){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setOrder(1);
return resolver;
}
}
Try using this setting in your application.properties file:
spring.thymeleaf.prefix=classpath:/static/

Resources