spring boot thymeleaf does not work with LiteDeviceDelegatingViewResolver - spring-boot

I am working with spring boot 2.0.1.RELEASE, using spring-boot-starter-thymeleaf. And I have two version of pages for PC and Mobile phone.
Here is a simple version of my project structure
project structure
I would like to have the site automatically detect the PC browser and Mobile phone browser so that it can map the same requesting URL to different pages according to the type of the browsers
The html file is very simple as the following.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>This is from PC web browser</p>
</body>
</html>
Here is the code of my controller.
#Controller
public class MyController {
#RequestMapping("/")
public String mainDefault(){
return "home";
}
#RequestMapping("/home")
public String main(){
return "home";
}
}
To detect the device I wrote the following configuration class
#Configuration
public class Config implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
#Bean
public LiteDeviceDelegatingViewResolver liteDeviceDelegatingViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
LiteDeviceDelegatingViewResolver resolver = new LiteDeviceDelegatingViewResolver(viewResolver);
resolver.setNormalPrefix("web/");
resolver.setMobilePrefix("mobile/");
resolver.setTabletPrefix("web/");
resolver.setOrder(1);
resolver.setEnableFallback(true);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new LayoutDialect());
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(".HTML5");
templateResolver.setCacheable(false);
return templateResolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor = new DeviceResolverHandlerInterceptor();
SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor = new SitePreferenceHandlerInterceptor();
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
registry.addInterceptor(deviceResolverHandlerInterceptor);
registry.addInterceptor(sitePreferenceHandlerInterceptor);
WebMvcConfigurer.super.addInterceptors(registry);
}
}
Then when I tried to run the Application. I always get the following error. It seems it can map to the right resource which is [/templates/web/home.html]. But it always say Could not open ServletContext resource [/templates/web/home.html]
If I visit from a mobile browser it map to [/templates/mobile/home.html] which is also correct.
Can any one help me? Thank you in advance.
2018-04-19 17:18:57.040 ERROR 17732 --- [nio-9020-exec-9] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-9020-exec-9] Exception processing template "web/home": An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:235) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
.........
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/templates/web/home.html]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:159) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.thymeleaf.spring5.templateresource.SpringResourceTemplateResource.reader(SpringResourceTemplateResource.java:103) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:223) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
... 52 common frames omitted
2018-04-19 17:18:57.047 DEBUG 17732 --- [nio-9020-exec-9] o.s.web.servlet.DispatcherServlet : Error rendering view [org.thymeleaf.spring5.view.ThymeleafView#139a953a] in DispatcherServlet with name 'dispatcherServlet'
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")

You are trying to configure something that is already being configured for you.
Make sure you have the spring-boot-starter-thymeleaf and that you have the spring-mobile-autoconfguration in your list of dependencies.
NOTE: I assume you are using the snapshot builds so that you are compatible with regards to Spring and Spring Boot version.
Remove your Config class
Add the following to your application.properties
spring.mobile.devicedelegatingviewresolver.normalPrefix=web/
spring.mobile.devicedelegatingviewresolver.tabletPrefix=web/
spring.thymeleaf
Restart your application

Related

Thymeleaf - return rendered template to String

I was using this solution as an example:
Can I render Thymeleaf templates manually from a String?
but after processing template I am getting not rendered template:
my template (plans/test.html):
<!doctype html>
<html lang="pl">
<div th:text="${loki}"></div>
</html>
Java code that should renderd the template:
#RestController
public class PlansPageRestController {
#Autowired
TemplateEngine myTemplateEngine;
#RequestMapping(value = {"/public/plans"}, method = RequestMethod.POST, produces = "application/json")
public Map<String,String> getPlans(#RequestParam Map<String, String> requestParams) {
Context ctx = new Context();
ctx.setVariable("loki", "Some test value");
String htmlTemplate = myTemplateEngine.process("plans/test.html", ctx);
Map<String,String> result = new HashMap<>();
result.put("html", htmlTemplate );
result.put("result", "success" );
return result;
}
}
but as a result I am getting content of plans/test.html so:
<!doctype html>
<html lang="pl">
<div th:text="${loki}"></div>
</html>
I am working with spring boot 3.0.0 and regarding to pom I am using thymeleaf:
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.0.RELEASE</version>
Can anyone help me in finding what I am doing wrong?
my thymeleaf configuration:
#Configuration
public class ThymeleafConfiguration implements WebMvcConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public TemplateEngine myTemplateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
engine.setDialect(new LayoutDialect());
return engine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/templates/");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
resolver.setCacheTTLMs(0L);
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
The reason your standard Thymeleaf expression is not being evaluated is because you have replaced the Thymeleaf "standard" dialect with the layout dialect:
engine.setDialect(new LayoutDialect());
(I should say "the Spring dialect", given you are using Spring-Thymeleaf.)
If you need to use the layout dialect, then you can add it to the engine - and still keep the standard dialect as well:
engine.addDialect(new LayoutDialect());
Or, you may not need the layout dialect at all (you do not use it in the sample HTML file in the question, but maybe you use it elsewhere). If that is the case, you can remove this line of code.
Just to add: The default Spring Boot settings should work without you needing to define any ThymeleafConfiguration class. But, again, there may be other places where you need to use a custom configuration (e.g. the UTF-8 encoding).

spring boot and thymeleaf for email only

I have a spring boot application that works correctly.
I need to add a functionality to send emails if certain actions happens.
I have added the thymeleaf code:
#Configuration
public class ThymeleafTemplateConfig {
#Bean
public SpringTemplateEngine springTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(htmlTemplateResolver());
return templateEngine;
}
#Bean
public FileTemplateResolver htmlTemplateResolver() {
FileTemplateResolver emailTemplateResolver = new FileTemplateResolver();
emailTemplateResolver.setPrefix(EMAILS_TEMPLATES_DIRECTORY);
//emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode("HTML");
emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
return emailTemplateResolver;
}
}
and below the code for handling the root url /
#Controller
public class IndexController {
#RequestMapping("/")
public String index() {
//return "main.xhtml";
return "operation.xhtml";
}
}
When I login to my site I redirect to "http://mysiteip/" and I have set as mapping that the "/ " is the operation.xhtml
so at the redirection or if I hit "http://mysiteip/" an error occurs.
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - : - : - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "operation.xhtml", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "operation.xhtml", template might not exist or might not be accessible by any of the configured Template Resolvers
Why the thymeleaf tries to render the view? I want to use thymeleaf only for the email part (send emails with html body).

WebMvcTest when Multiple TemplateResolver

I have added a second TemplateResolver because I would like to use also Thymeleaf to serve Template for Plain-Text Templates. When starting application it works fine, the Thymeleaf templates are being resolved.
But from now my #WebMvcTests are not working. I says, that:
Error resolving template [disclaimer/view], template might not exist or might not be accessible by any of the configured Template Resolvers
The TemplateResolver are configures like:
#Configuration
public class SitemapViewResolverConfiguration {
#Bean
public ITemplateResolver thymeleafTextTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("/templates/sitemap/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCheckExistence(TRUE);
return templateResolver;
}
}
#Configuration
public class PageViewResolverConfiguration {
#Bean
public ITemplateResolver thymeleafHtmlTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("classpath:/templates/view/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCheckExistence(TRUE);
return templateResolver;
}
}
My template structure is as follows:
The test class is:
#RunWith(SpringRunner.class)
#WebMvcTest(DisclaimerController.class)
public class DisclaimerControllerTest {
#Autowired MockMvc mvc;
#Test
public void testGet() throws Exception {
mvc.perform(get("/disclaimer")
.andExpect(status().isOk())
.andExpect(view().name("disclaimer/view"))
;
}
}
Error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template [disclaimer/view], template might not exist or might not be accessible by any of the configured Template Resolvers
Okay, I found the problem. As I use #WebMvcTest it didn't load my TemplateResolver. As before I relied on Spring Boot automatic resolver, I am using now two selfmade resolver. But they are not picked up by the test, that's why MockMvc could not resolve it.
I fixed it by adding Configuration as Import:
#RunWith(SpringRunner.class)
#WebMvcTest({PageViewResolverConfiguration.class, DisclaimerController.class})
public class DisclaimerControllerTest {
...
}

unable to load static file in spring 4

Hi I am trying to load the jquery file in jsp but getting 404
Find below the project structure
I have configured the following in configuration too
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "common.spring.controller" })
public class WebConfig {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
//viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/resources/");
}
}
below is how i include file in jsp
<script type="text/javascript" src="/resources/jsFiles/jquery-3.3.1.min.js"></script>
Dono why its not working.
UPDATE: I am able to see the resources folder under target/project snapshot/resources but getting 404 for url
http://localhost:8080/resources/jsfiles/jquery-3.3.1.min.js
directly hitting below Url didnt work either
http://localhost:8080/Sample/resources/jsfiles/jquery-3.3.1.min.js
Finally was able to resolve the issue
removing addResourceHandlers method and including the below code did the trick
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configure) {
configure.enable();
}
in jsp i used the include like below
<script type="text/javascript" src="resources/jsFiles/jquery-3.3.1.min.js"></script>

Migration of thymeleaf version 2.14 to 3.0 not finding message properties

Thymeleaf version 2.14 was picking up message.properties file correctly. After migration to 3.0,it is giving error ??hello.MESSAGE_en_US??.
But in javacode by autowiring messsagesource
messageSource.getMessage( "hello.MESSAGE",null, Locale.getDefault()) ->Hello App
The project structure
src/main/
-java
-com.cando
-controllers
-resources
-messages_en.properties
-spring.properties
-webapp
-WEB-INF
-templates
-index.html
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Helllo</title>
</head>
<body>
<p th:text="#{hello.MESSAGE}">Hello App!</p>
</body>
</html>
messages_en.properties
hello.MESSAGE=Hello App
config
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setBasename("messages");
resourceBundleMessageSource.setBasename("spring");
return resourceBundleMessageSource;
}
In your config file you setBasename() to messages and then override value to spring. I think ResourceBundleMessageSource just doesn't see your messages file, and you should add it like: setBasenames("messages","spring"). Give it a try and tell if it works :)
I think i found solution
While you configure SpringTemplateEngine it's possible to explicitly set messageSource and messageResolver.
For me, this works.
My code in kotlin:
#Configuration
#ComponentScan
open class ThymeleafConfig {
#Autowired
lateinit var applicationContext: ApplicationContext
#Autowired
lateinit var messageSource: MessageSource
#Autowired
lateinit var messageResolver: SpringMessageResolver
#Bean
open fun viewResolver(): ViewResolver = ThymeleafViewResolver().apply
{
templateEngine = templateEngine()
characterEncoding = "UTF-8"
}
fun templateEngine(): TemplateEngine = SpringTemplateEngine().apply {
enableSpringELCompiler = true
setTemplateResolver(templateResolver())
setMessageSource(messageSource)
addMessageResolver(messageResolver)
}
fun templateResolver(): ITemplateResolver = SpringResourceTemplateResolver().apply {
prefix = "file:./web/templates/"
suffix = ".html"
templateMode = TemplateMode.HTML
cacheTTLMs = 0
isCacheable = false
setApplicationContext(applicationContext)
}
}
#Component
class MessageResolverImpl: SpringMessageResolver() { }
and messageSource bean:
#Bean
open fun messageSource(): MessageSource {
val source = ReloadableResourceBundleMessageSource()
source.setBasename("classpath:messages")
source.setUseCodeAsDefaultMessage(true)
source.setDefaultEncoding("UTF-8")
return source
}
Hope this will help you

Resources