TemplateEngine not Finding file within templates folder - java

I'm building a SpringBoot app that loads internationalisation messages from Database.
(Followed this tutorial)
From that, I had to create a ThymeleafConfiguration class and set a SpringTemplateEngine Bean. The tutorial gave only a rough idea about this configuration (only configured the message source, but not the other templateengine configurations), so it broke my controller page rendering (controller is now returning string instead of view).
I'm trying to configure the rest of my TemplateEngine (such as the TemplateResolver, which I think is the reason why the rendering is not correct), however, I can't figure out how to do it correctly [I keep getting "An error happened during template parsing (template: "ServletContext resource [/templates/login.html]")" messages].
How to configure SpringTemplateEngine correctly?
My configuration so far:
#Configuration
public class ThymeleafConfiguration implements WebMvcConfigurer, ApplicationContextAware{
private ApplicationContext applicationContext;
#Autowired
private DatabaseMessageSource databaseMessageSource;
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(thymeleafTemplateEngine());
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.setTemplateEngineMessageSource(databaseMessageSource);
return engine;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Controller class:
#Controller
public class ApplicationController {
#RequestMapping("/home")
public String home() {
return "/home.html";
}
#RequestMapping("/core/index")
public String index() {
return "/core/index.html";
}
#RequestMapping("/login")
public String login() {
return "login";
}

The problem is with
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/templates/"); // Here!!!
resolver.setSuffix(".html");
return resolver;
}
Since the templates are within src/main/resources, it has to be pointed to classpath, as follows:
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/templates/"); // It works after adding 'classpath:'
resolver.setSuffix(".html");
return resolver;
}
Also, there's a little change from the interface to a implementation class that should not interfere with the result.

Related

Sending emails in different languages using Thymeleaf

I am sending emails using Thymeleaf. I've set up the templates that I need and it works perfectly. I'm now trying to translate this email depending on the receiver's language. I know the language I need to use through a previously recovered variable.
I'm using properties files to set up the variables that will be translated.
So far, I have three languages: French, English, and Chinese. So I have three properties files :
mail.messages_fr_FR
mail.messages_en_US
mail.messages_zh_CN
I have the following methods in my email configuration class :
#Bean
public ReloadableResourceBundleMessageSource emailMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("mailMessages");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
I have enabled the Mvc :
#SpringBootApplication
#EnableWebMvc
public class AccessRequestEventHookApplication extends SpringBootServletInitializer implements WebMvcConfigurer {
I have a method in my email configuration class which is the following :
public void sendMessageUsingThymeleafTemplate(String to, String subject, Map<String, Object> templateModel, String appName)
throws MessagingException {
Context thymeleafContext = new Context();
thymeleafContext.setVariables(templateModel);
String htmlBody;
if (appName == "Test1"){
htmlBody = thymeleafTemplateEngine.process("Test1-template", thymeleafContext);
} else {
htmlBody = thymeleafTemplateEngine.process("Test2-template", thymeleafContext);
}
sendHtmlMessage(to, subject, htmlBody);
}
This function is then called in my controller.
I also have my template resolver and engine as follow :
#Bean
#Primary
public ITemplateResolver thymeleafTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("mail-templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setTemplateEngineMessageSource(emailMessageSource());
return templateEngine;
}
I tried changing the locale resolver type with cookie, acceptLanguage, it still does not work.
If I change the language on my computer (from French to English for example), it works, the English translation is used. But I can't figure out how to change the locale variable directly in the code.
As it is sent to a user, I can not base myself on their locale.
If I was not clear, please let me know and I will provide any info needed.
Thanks !
You need to set the locale on the Context object you create for Thymeleaf:
Context thymeleafContext = new Context();
thymeleafContext.setLocale(locale);
If you are calling your sendMessageUsingThymeleafTemplate method from a web controller, you can do this as well:
#Controller
public class MyController {
private final ServletContext servletContext;
public MyController(ServletContext servletContext) {
this.servletContext = servletContext;
}
#GetMapping
public void myControllerMethod(HttpServletRequest request,
HttpServletResponse response,
Locale locale) {
WebContext context = new WebContext(request, response, servletContext);
context.setLocale(locale);
service.sendMessageUSingThymeleafTemplate(context, ...);
}
}

DataSourceInitializer and Spring MVC

I am studying java and spring and try to make my first spring mvc app. At first I made console app without spring mvc I used just only dao and service layers with JdbcTemplate. I have config file where I make Beans for DataSource and DataSourceInitializer for initializing database.
#EnableWebMvc
#EnableTransactionManagement
#ComponentScan("ru.stepev")
#PropertySource("classpath:config.properties")
public class UniversityConfig implements WebMvcConfigurer {
#Value("${driver}")
private String driver;
#Value("${url}")
private String url;
#Value("${user}")
private String user;
#Value("${pass}")
private String pass;
#Value("${schema}")
private Resource schema;
#Value("${data}")
private Resource data;
#Bean
public JdbcTemplate jdbcTamplate(DataSource dateSourse) {
return new JdbcTemplate(dateSourse);
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(user);
dataSource.setPassword(pass);
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
resourceDatabasePopulator.addScript(schema);
resourceDatabasePopulator.addScript(data);
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(dataSource);
dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);
return dataSourceInitializer;
}
private final ApplicationContext applicationContext;
public UniversityConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("/WEB-INF/view/");
templateResolver.setSuffix(".jsp");
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
registry.viewResolver(resolver);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
}
All shemes and data I stores in resourse folder. Without mvc everything were be ok. But when I have added MVC then I got java.io.FileNotFoundException org.springframework.jdbc.datasource.init.CannotReadScriptException: Cannot read SQL script from ServletContext resource [/schema.sql]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/schema.sql]. DataSourceInitializer can't find schema.sql when I create my datebase. I have tried to solve this problem but I couldn't. I have next structure of project.enter image description here
I wil be very thaks for help me with this problem and sorry for my bad english.
I've found my mistake. I had an exception because I didn't put the word classpath before the file name in the properties file.
schema=classpath:schema.sql
data=classpath:data.sql

Spring MVC loads blank webpage, but '/' is configured to load homepage. Setup done using no xml

I'm trying to build a Spring MVC application, using no XML. To load the application I am using a tomcat server. I want the homepage "home.html" to load, when I go to http://localhost:8080/.
I have followed many other spring mvc helloworld tutorials, but I can't see what I'm doing wrong. The webpage that loads, when I run the application is completely blank. When I inspect the html, it has an empty body. Any help is much appreciated.
Initialization class
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ApplicationConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebMvcConfig.class};
}
#Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
DelegatingFilterProxy securityFilterChain = new DelegatingFilterProxy("springSecurityFilterChain");
return new Filter[] {characterEncodingFilter, securityFilterChain};
}
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("defaultHtmlEscape", "true");
registration.setInitParameter("spring.profiles.active", "default");
}
}
MVC configuration
#Configuration
class WebMvcConfig extends WebMvcConfigurationSupport {
private static final String MESSAGE_SOURCE = "/WEB-INF/i18n/messages";
private static final String VIEWS = "/WEB-INF/views/";
private static final String RESOURCES_LOCATION = "/resources/";
private static final String RESOURCES_HANDLER = RESOURCES_LOCATION + "**";
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
requestMappingHandlerMapping.setUseTrailingSlashMatch(false);
return requestMappingHandlerMapping;
}
#Bean(name = "messageSource")
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
return messageSource;
}
#Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix(VIEWS);
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new SpringSecurityDialect()); //TODO no using Spring security
templateEngine.addDialect(new Java8TimeDialect());
return templateEngine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
thymeleafViewResolver.setTemplateEngine(templateEngine());
thymeleafViewResolver.setCharacterEncoding("UTF-8");
return thymeleafViewResolver;
}
#Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(RESOURCES_HANDLER).addResourceLocations(RESOURCES_LOCATION);
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* Handles favicon.ico requests assuring no <code>404 Not Found</code> error is returned.
*/
#Controller
static class FaviconController {
#RequestMapping("favicon.ico")
String favicon() {
return "forward:/resources/images/favicon.ico";
}
}
}
Home controller
#Controller
public class HomeController {
#RequestMapping(value = "/")
public String index() {
return "home";
}
}
Here is what my views look like

Spring Boot internationalization (messages.properties)

I'm trying to simply add the version number of an application to a Thymeleaf fragment. I'm using Spring Boot 1.2.5. If I have a file named /resources/messages.properties defined like this:
application.version=1.0.0
And I have a Thymeleaf view with the following fragment:
Application Version: <span th:text="#{application.version}">
It's displaying something like ??application.version_en_US?? instead of 1.0.0. (I also have files named messages_en.properties and messages_en_US.properties in the classpath with the same contents too.) I am really not sure how to resolve this problem... I've spent hours on something which seems incredibly trivial...
Application.java
#SpringBootApplication
#ComponentScan(basePackages = {"org.application"})
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, ThymeleafAutoConfiguration.class})
#PropertySources(value = {#PropertySource("classpath:website.properties")})
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
WebConfig.java
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public SpelAwareProxyProjectionFactory projectionFactory() {
return new SpelAwareProxyProjectionFactory();
}
#Bean
public SessionHandler sessionHandler() {
return new SessionHandler();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/web/auth/login").setViewName("auth/login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations(
"/resources/");
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
#Bean
public CookieLocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
return localeResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
return new ResourceBundleMessageSource();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
// Locale change interceptor
registry.addInterceptor(localeChangeInterceptor());
// Utility interceptor which helps with the "active" link styles in the navigation. --mm
registry.addInterceptor(new BaseInterceptor());
// Expire session after a period of time
registry.addInterceptor(sessionHandler());
}
}
ThymeleafConfig.java
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
// must use Legacy HTML5 as the template, otherwise Handlebars will not parse!
//
// this should hopefully be fixed in Thymeleaf 3.0
resolver.setTemplateMode("LEGACYHTML5");
resolver.setCacheable(false);
return resolver;
}
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
// Add Spring security
Set<IDialect> dialects = new HashSet<IDialect>();
engine.setAdditionalDialects(dialects);
engine.addDialect(new SpringSecurityDialect());
return engine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
viewResolver.setViewNames(new String[]{"*"});
viewResolver.setCache(false);
return viewResolver;
}
}
Will buy a virtual round of shots to whomever can resolve this issue...
i guess you could always add this in your templateEngine method:
engine.addMessageResolver(new StandardMessageResolver());
or engine.setMessageResolver(new StandardMessageResolver());
Also,from the design perspective,i would suggest you to try using the autoconfiguration for thymeleaf(removing the exclude),and many other stuff which spring boot provides automatically for you.

Spring JavaConfig is not catching PageNotFound?

I am trying to setup a page not found catch in my Spring WebMVCConfig bit its not working..
here is my config:
#Configuration
#EnableWebMvc
#PropertySource("classpath:application.properties")
#Import(DatabaseConfig.class)
#ImportResource("/WEB-INF/spring/applicationContext.xml")
public class WebMVCConfig extends WebMvcConfigurerAdapter {
private static final String MESSAGE_SOURCE = "/WEB-INF/classes/messages";
private static final Logger logger = LoggerFactory.getLogger(WebMVCConfig.class);
#Autowired
Environment env;
#Bean
public ViewResolver resolver() {
UrlBasedViewResolver url = new UrlBasedViewResolver();
url.setPrefix("/WEB-INF/view/");
url.setViewClass(JstlView.class);
url.setSuffix(".jsp");
return url;
}
#Bean(name = "messageSource")
public MessageSource configureMessageSource() {
logger.debug("setting up message source");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver lr = new SessionLocaleResolver();
lr.setDefaultLocale(Locale.ENGLISH);
return lr;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
logger.debug("setting up resource handlers");
registry.addResourceHandler("/resources/").addResourceLocations("/resources/**");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
logger.debug("configureDefaultServletHandling");
configurer.enable();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
public #Bean HandlerExceptionResolver exceptionResolver() {
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "404");
mappings.put(DataAccessException.class.getName(), "dataAccessFailure");
mappings.put(TransactionException.class.getName(), "dataAccessFailure");
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
resolver.setExceptionMappings(mappings);
return resolver;
}
#Bean
public DefaultAnnotationHandlerMapping mapping() {
DefaultAnnotationHandlerMapping m = new DefaultAnnotationHandlerMapping();
m.setDetectHandlersInAncestorContexts(true);
return m;
}
}
now if I put in a URL that is not mapped I would think it would goto my 404.jsp page?
The way to configure a custom error page (for 404 or any other error code) is in the web.xml...
<error-page>
<error-code>404</error-code>
<location>/my-custom-page-not-found.html</location>
</error-page>
The url can be a static page, a custom jsp or even a Spring controller.
You can use #ExceptionHandler annotated method or #ControllerAdvice class.
Several methods are described in this post of the forum.
Alternatively, you can also refer Spring Docs - Handling exceptions section.

Categories

Resources