Is it possible to pass object let's say User (who contains 3 String attributes - name, password, detail) to thymeleaf template via context myContext.setVariable("user", myUser) and access it attributes from template like this <div th:text="${user.name}"/> ?
If so how can I do that ?
My object contains a lot of attributes and I am trying to avoid creating context with a lot of variables.
I am very new to thymeleaf so thank you for any help.
If you are using spring and thymeleaf then they should work for you like a charm. In the case it's as simple as:
private static final VAR_USER = "user"
#Autowired
private SpringTemplateEngine templateEngine;
...
public void method(User user,...) {
Map<String, Object> variables;
variables.put(VAR_USER, user);
context.setVariables(variables);
org.thymeleaf.context.Context context = new Context(locale);
String evaluated = templateEngine.process("myTemplate", context);
}
where myTemplate refers to resources/mails/myTemplate.html and it's content looks like:
<p th:text="#{email.userActivity.greeting}">Hello</p>
<p th:text="#{email.userActivity.text1}">Following user activity...</p>
<ul>
...
<li th:text="#{email.userActivity.phone(${user.phoneNumber}?: #{error.not.provided})}">Phone number:</li>
<li th:text="#{email.userActivity.membershipNumber(${user.membershipNumber}?: #{error.not.provided})}">Membership number:</li>
...
</ul>
<p th:text="#{email.userActivity.text2}">Thanks for taking care of this demand within the agreed period!</p>
<p th:text="#{email.userActivity.text3}">Regards</p>
and my User entity
public class User implements Serializable {
...
#Column(name = "membership_number")
private String membershipNumber;
#Column(name = "phone_number")
private String phoneNumber;
...
}
Then, my Thymeleaf configuration:
package my.package.config;
import my.package.MyTemplateEngine;
import org.apache.commons.lang3.CharEncoding;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.StringTemplateResolver;
#Configuration
public class ThymeleafConfiguration {
private MyTemplateEngine templateEngine;
#Bean
#Description("Thymeleaf template resolver serving HTML 5 emails from template file")
public ITemplateResolver htmlTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
emailTemplateResolver.setCharacterEncoding(CharEncoding.UTF_8);
emailTemplateResolver.setCheckExistence(true);
return emailTemplateResolver;
}
#Description("Thymeleaf template resolver serving HTML 5 emails from input string")
#Bean
public ITemplateResolver stringTemplateResolver() {
final StringTemplateResolver templateResolver = new StringTemplateResolver();
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
#Bean
public MyTemplateEngine createTemplateEngine() {
templateEngine = new MyTemplateEngine();
return templateEngine;
}
}
and the version of Thymeleaf that I use:
<properties>
...
<thymeleaf-layout-dialect.version>2.2.1</thymeleaf-layout-dialect.version>
<thymeleaf.version>3.0.6.RELEASE</thymeleaf.version>
...
</properties>
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
...
</dependencies>
Related
Initially, I started the project with IntelliJ Idea Tomcat Local Server configuration (everything worked), but decided to use Spring Boot Application. I added the main class, changed pom.xml (delete spring-context and add spring-boot-starter-parent, spring-boot, spring-boot-starter-tomcat, spring-boot-starter-web, spring-boot-autoconfigure), after that application runs , GET-Method works, but POST - not supported. Help me please!!! Thank you!
Main.class
#SpringBootApplication(scanBasePackageClasses = {SpringConfig.class})
public class Main extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
Pom.xml to springBoot
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
Main configuration class
#Configuration
#ComponentScan("ru")
#PropertySource("classpath:application.properties")
#EnableWebMvc
public class SpringConfig implements WebMvcConfigurer {
private final ApplicationContext applicationContext;
#Autowired
public SpringConfig(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/*
* Dispatcher configuration for serving static resources
*/
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
registry.addResourceHandler("/images/**").addResourceLocations("/images/");
registry.addResourceHandler("/webjars/bootstrap/4.6.0/css/**").addResourceLocations("/webjars/bootstrap/4.6.0/css/bootstrap.min.css");
registry.addResourceHandler("assets/select2-develop/**").addResourceLocations("/assets/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
}
/*
* Message externalization/internationalization
*/
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("Messages");
return messageSource;
}
#Bean
public SpringResourceTemplateResolver templateResolver(){
// SpringResourceTemplateResolver automatically integrates with Spring's own
// resource resolution infrastructure, which is highly recommended.
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
// HTML is the default value, added here for the sake of clarity.
templateResolver.setTemplateMode(TemplateMode.HTML);
// Template cache is true by default. Set to false if you want
// templates to be automatically updated when modified.
templateResolver.setCacheable(true);
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine(){
// SpringTemplateEngine automatically applies SpringStandardDialect and
// enables Spring's own MessageSource message resolution mechanisms.
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
// Enabling the SpringEL compiler with Spring 4.2.4 or newer can
// speed up execution in most scenarios, but might be incompatible
// with specific cases when expressions in one template are reused
// across different data types, so this flag is "false" by default
// for safer backwards compatibility.
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("UTF-8");
registry.viewResolver(resolver);
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
dataSource.setUsername("over");
// dataSource.setPassword("postgres"); Можно установить пароль для базы данных.
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
DispatcherSerlvet config
public class MySpringMvcDispatcherSerlvetIntitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
/* Класс знает теперь где находится spring конфигурация */
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringConfig.class};
}
/* Все http запросы от пользователя посылаем на dispatcher servlet */
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
public void onStartup(ServletContext aServletContext) throws ServletException {
super.onStartup(aServletContext);
registerCharacterEncodingFilter(aServletContext);
registerHiddenFieldFilter(aServletContext);
}
private void registerHiddenFieldFilter(ServletContext aContext) {
aContext.addFilter("hiddenHttpMethodFilter",
new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null ,true, "/*");
}
private void registerCharacterEncodingFilter(ServletContext aContext) {
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
FilterRegistration.Dynamic characterEncoding = aContext.addFilter("characterEncoding", characterEncodingFilter);
characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
}
}
This is my exeption :
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.logException - Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
Controller
#RequestMapping("/categories")
#Controller
public class CategoriesController {
private CategoriesDao categoriesDAO;
#Autowired
public void setCategoriesDAO(CategoriesDao categoriesDAO)
{
this.categoriesDAO = categoriesDAO;
}
#GetMapping
public String index(Model model) {
model.addAttribute("category", new Category());
model.addAttribute("categories", categoriesDAO.index());
return "categories/index";
}
#PostMapping
public String addCategory(#ModelAttribute("category") #Valid Category category,
BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors()) {
model.addAttribute("categories", categoriesDAO.index());
return "categories/index";
}
categoriesDAO.addCategory(category);
return "redirect:categories";
}
#DeleteMapping("/{id}")
public String deleteCategory(#PathVariable("id") int id) {
categoriesDAO.deleteCategory(id);
return "redirect:/categories";
}
#GetMapping("/{id}/edit")
public String editCategory(Model model, #PathVariable("id") int id) {
model.addAttribute("editCategory", categoriesDAO.editCategory(id));
return "categories/edit";
}
#PatchMapping("/{id}")
public String updateCategory(#ModelAttribute("editCategory") Category updateCategory,
#PathVariable("id") int id) {
categoriesDAO.updateCategory(id, updateCategory);
return "redirect:{id}/edit";
}
}
To implement the configure method in the main class you could do:
#SpringBootApplication
public class Main extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder builder) {
return builder.sources(Main.class);
}
}
I think these dependencies should be enough (assuming you are using Thymeleaf):
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
EDIT: Your ModelAttribute 'Category' might not be parsed successfully. Either the mapping fails, or maybe the validation? You might find out by adding an error handler and/or debugging the sessions.
Other ideas: Use #RestController instead of #Controller. Try to make the request mappings more explicit. Instead of on the class, put one on each method:
#RequestMapping(value = "/categories", produces = "application/json", method=RequestMethod.GET)
public String index(Model model) {
...
#RequestMapping(value = "/categories", produces = "application/json", method=RequestMethod.POST)
public String addCategory(#ModelAttribute("category") #Valid Category category,
BindingResult bindingResult, Model model) {
...
Thanks everyone! I found answer on my question. It was some problems with HiddenHttpMethodFilter. I just added this code in my Spring Config and extra dependencies was . It stared to work!!!
public FilterRegistrationBean<HiddenHttpMethodFilter> hiddenHttpMethodFilter() {
FilterRegistrationBean<HiddenHttpMethodFilter> filterRegistrationBean =
new FilterRegistrationBean<HiddenHttpMethodFilter>(new HiddenHttpMethodFilter());
filterRegistrationBean.setUrlPatterns(Collections.singletonList("/*"));
return filterRegistrationBean;
}
I have an XML file that I am trying to read that has elements with attributes. I have tried from multiple examples, but the fields in my class always end up as null as shown below:
Data [type=null, value=null]
Data [type=null, value=null]
Data [type=null, value=null]
Below is the cut down example code of my issue.
Here is an example XML file located in the src/main/resources/data directory (data.xml):
<?xml version="1.0" encoding="UTF-8"?>
<list>
<data type="shopping" value="milk" />
<data type="shopping" value="eggs" />
<data type="TODO" value="return books to library" />
</list>
Below is my domain class for the XML data (Data.java):
package com.example.demo.springbatchtest.domain;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "data")
public class Data {
private final String type;
private final String value;
public Data(String type, String value) {
this.type = type;
this.value = value;
}
#XmlAttribute(name = "type")
public String getType() {
return type;
}
#XmlAttribute(name = "value")
public String getValue() {
return value;
}
#Override
public String toString() {
return "Data [type=" + type + ", value=" + value + "]";
}
}
Here is my Spring Batch job configuration file (JobConfiguration.java):
package com.example.demo.springbatchtest.configuration;
import java.util.HashMap;
import java.util.Map;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.xml.StaxEventItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.xstream.XStreamMarshaller;
import com.example.demo.springbatchtest.domain.Data;
#Configuration
public class JobConfiguration {
#Autowired
public JobBuilderFactory jobBuilderFactory;
#Autowired
StepBuilderFactory stepBuilderFactory;
#Bean
public StaxEventItemReader<Data> dataItemReader() {
XStreamMarshaller unmarshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<>();
aliases.put("data", Data.class);
unmarshaller.setAliases(aliases);
StaxEventItemReader<Data> reader = new StaxEventItemReader<>();
reader.setResource(new ClassPathResource("/data/data.xml"));
reader.setFragmentRootElementName("data");
reader.setUnmarshaller(unmarshaller);
return reader;
}
#Bean
public ItemWriter<Data> dataItemWriter() {
return items -> {
for (Data item : items) {
System.out.println(item.toString());
}
};
}
#Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.<Data, Data>chunk(10)
.reader(dataItemReader())
.writer(dataItemWriter())
.build();
}
#Bean
public Job job() {
return jobBuilderFactory
.get("job")
.start(step1())
.build();
}
}
Here is my main Spring Boot class (SpringBatchTestApplication.java):
package com.example.demo.springbatchtest;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
#EnableBatchProcessing
public class SpringBatchTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchTestApplication.class, args);
}
}
Here is my pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>SpringBatchTestSpringBoot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBatchTestSpringBoot</name>
<description>Spring Batch Test with Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
It sounds like JAX-B is unable to set the Data.type and value attributes.
By default it will be looking for a getter/setter pair for each attribute, otherwise they will be treated as read-only.
The alternative would be to use field level access - #XmlAccessorType(XmlAccessType.FIELD)
Found this tutorial which helped me get the example code to work.
https://walkingtechie.blogspot.com/2017/03/spring-batch-xml-file-to-mysql-example.html
The key was to a Converter class to parse out the attributes. So in my example, I added a call set a Converter class in the dataItemReader() in the JobConfiguration class.
import com.example.demo.springbatchtest.converter.DataConverter;
...
#Autowired
private DataConverter dataConverter;
...
#Bean
public StaxEventItemReader<Data> dataItemReader() {
XStreamMarshaller unmarshaller = new XStreamMarshaller();
Map<String, Class> aliases = new HashMap<>();
aliases.put("data", Data.class);
unmarshaller.setAliases(aliases);
unmarshaller.setConverters(dataConverter); // from Walking Techie
StaxEventItemReader<Data> reader = new StaxEventItemReader<>();
reader.setResource(new ClassPathResource("/data/data.xml"));
reader.setFragmentRootElementName("data");
reader.setUnmarshaller(unmarshaller);
return reader;
}
And then I extended the Converter class to handle the attributes.
package com.example.demo.springbatchtest.converter;
import org.springframework.stereotype.Component;
import com.example.demo.springbatchtest.domain.Data;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
#Component
public class DataConverter implements Converter {
#Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
}
#Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
String type = reader.getAttribute("type");
String value = reader.getAttribute("value");
return new Data(type, value);
}
#Override
public boolean canConvert(Class type) {
return type.equals(Data.class);
}
}
The program now outputs the following:
Data [type=shopping, value=milk]
Data [type=shopping, value=eggs]
Data [type=TODO, value=return books to library]
Update:
Thanks all for your answer. I still have problems, since it's not returning the result I hoped. Is it a problem of the placement of the files? They are organized like this: i63.tinypic.com/2by8g2.png
Thanks again in advance.
I'm studying in order to create a web application using Spring Boot and JDBC (without JPA); I'm having several problems doing it.
I don't understand how to correctly create the controller, and how to make it return the .jsp pages that I want.
I searched lots and lots solutions, but just can't find the right one.
This is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>---myprojectname</groupId>
<artifactId>tesieasy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>tesieasy</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
My application.properties:
> spring.h2.console.enabled=true
server.port=8000
spring.view.prefix=/WEB-INF/jsp/
spring.view.suffix=.jsp
My ServetInizializer:
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TesieasyApplication.class);
}
}
My entity:
import java.util.Date;
import java.text.SimpleDateFormat;
public class Journal {
private Long id;
private String title;
private Date created;
private String summary;
private SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy");
/** costruttori**/
public Journal() {}
public Journal(Long id, String title, String summary, Date date){
this.id = id;
this.title = title;
this.summary = summary;
this.created = date;
}
/** getters and setters omitted **/
My service:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import name of my package-.entity.Journal;
#Service
public class JournalService {
private static final Logger log = LoggerFactory.getLogger(JournalService.class);
#Autowired
JdbcTemplate jdbcTemplate;
public void insertData() {
log.info("> Table creation");
jdbcTemplate.execute("DROP TABLE JOURNAL IF EXISTS");
jdbcTemplate
.execute("-cut for brevity-");
log.info("> Done.");
}
public List<Journal> findAll() {
List<Journal> entries = new ArrayList<>();
jdbcTemplate
.query("SELECT * FROM JOURNAL",
new Object[] {}, (rs, row) -> new Journal(rs.getLong("id"), rs.getString("title"),
rs.getString("summary"), new Date(rs.getTimestamp("created").getTime())))
.forEach(entry -> entries.add(entry));
return entries;
}
public int countEntries() {
int rowCount = this.jdbcTemplate.queryForObject("SELECT count(*) FROM JOURNAL", Integer.class);
return rowCount;
}
public Journal insertJournal() {
Journal journal = null;
this.jdbcTemplate.update(
-cut for brevity-");
return journal;
}
}
One of my .jsp pages:
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>A title</title></head>
<body>
Hello! JSP is working fine!
</body>
</html>
And finally my controller:
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import *my package name.entity.Journal;
import *my package name.service.JournalService;
#RestController
public class HelloController {
#Autowired
private JournalService journalService;
#RequestMapping(value="/")
public String home() {
return "home";
}
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
#RequestMapping("/findAll")
public String index() {
List<Journal> all;
all = journalService.findAll();
return all.toString();
}
#RequestMapping("/count")
public int countElements() {
int count;
count = journalService.countEntries();
return count;
}
#RequestMapping("/insert")
public Journal insertElements() {
Journal insert;
insert = journalService.insertJournal();
return insert;
}
}
What I understood:
I don't need a web.xml file
How to connect the service with the controller (even if my findAll method return my data into {}, and I have to understand how to write it properly)
What I don't understand:
Why it's not linking correctly to my JSP pages - what do I need?
Do I need a Model and View?
I hope you can give me some advice and/or some examples right for my scope.
Thanks in advance and best regards!
Solution:
Step1: change the #RestController to #Controller
Step2: for URL that you want to return a view (e.g. greeting.jsp)
#RequestMapping("/greeting")
public String greeting() {
return "greeting";
}
Step3: for URL that you want to return a JSON object, no view (perhaps an AJAX call)
#RequestMapping("/findAll")
#ResponseBody
public List<Journal> index() {
List<Journal> all = journalService.findAll()
return all;
}
OR this
#RequestMapping("/findAll")
#ResponseBody
public Model index(Model model) {
List<Journal> all =journalService.findAll();
model.addAttribute("journalList", all);
return model;
}
Step4: for URL that you want the view and the model (e.g. a java object)
ref Rob Baily sample code
#RequestMapping("/findAll")
public String index(Model model) {
List<Journal> all;
all = journalService.findAll();
model.addAttribute("journalList", all);
return "listall";
}
and you can access the object in your listall.jsp by using ${journalList}
Explanation:
When you use #RestController on your class, it is equivalent to adding #ResponseBody for every method in your class. Thus, it will always return an object.toString() on the page and not linking to your JSP.
This code uses Spring 4’s new #RestController annotation, which marks the class as a controller where every method returns a domain object instead of a view. It’s shorthand for #Controller and #ResponseBody rolled together.
ref: https://spring.io/guides/gs/rest-service/
jsp sample reference: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-web-jsp
As the comment points out RestController is not correct if you want to return a view to be rendered. A RestController returns the object as JSON rather that an id for a view which is used for AJAX requests. Just use the Controller annotation if you want to return a view.
You do not need a View as it should route the return value to the JSP page. If you want to use JSP tags then yes you will need a Model and you will need to add objects to your Model object. See the example below. The first method should go to welcome.jsp and have no model available. The second method should go to listall.jsp and make a journalList attribute available for use in JSP tags.
#Controller
public class ApplicationController {
#RequestMapping("/")
public String index() {
return "welcome";
}
#RequestMapping("/findAll")
public String index(Model model) {
List<Journal> all;
all = journalService.findAll();
model.addAttribute("journalList", all);
return "listall";
}
}
I use springfox-swagger2 for my Spring MVC REST API. Everything works good with swagger but my problem is I cannot add additional information to my swagger documentation.
Maven Dependency:
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
My Swagger config class:
#Configuration
#EnableWebMvc
#EnableSwagger2
#ComponentScan("path.to.controller.package")
public class SwaggerConfig {
#Bean
public Docket customImplementation() {
return new Docket(DocumentationType.SPRING_WEB).apiInfo(apiInfo());
}
#Bean
public UiConfiguration uiConfig() {
return UiConfiguration.DEFAULT;
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("Service API", "Simple REST Service", "0.0.1",
"mail#mail.com", "mail#mail.com", " ", " ");
return apiInfo;
}
}
My controller class:
#RestController
#RequestMapping("/persons")
public class PersonController {
Logger LOGGER = LoggerFactory.getLogger(PersonController.class);
#RequestMapping(value = "/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
#ApiOperation(value = "doStuff", response = Person.class)
#ApiImplicitParams({#ApiImplicitParam(name="Authorization", value="MY DESCRIPTION")})
public #ResponseBody Person getPerson(#PathVariable String id,
#RequestHeader(value = "Authorization") String authToken) throws Exception {
//do things and return
}
}
So, calling the swagger-ui the controller is shown, the method, everything except my additional infos defined in #ApiOperation and #ApiImplicitParams. Does anyone have an idea from where the problem can come from? The params are also not in the JSON file which is created from swagger.
Try to replace your customImplementation() method by:
#Bean
public Docket customImplementation() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.build()
.apiInfo(apiInfo());
}
Build the project, and then your additional infos should appear.
EDIT: I don't know if it makes any difference, but I am using these dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.1.2</version>
</dependency>
I'm new at Spring and thymeleaf, I was working with JSF + Facelets so the method I've choosen thymeleaf layout dialect since it's very similar to Facelets, but, for some reason it's not working in my simple project.
I have this in my config files
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(WebConfig.class);
}
}
WebConfig.java
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#Import(ThymeLeafConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
my TyhmeLeafConfig
import nz.net.ultraq.thymeleaf.LayoutDialect;
import org.springframework.context.annotation.Bean;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
public class ThymeLeafConfig {
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setOrder(1);
resolver.setCacheable(false);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setDialect(new LayoutDialect());
return templateEngine;
}
#Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
}
The layout.html file
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<title>Layout page</title>
<script src="js/jquery.js"></script>
</head>
<body>
<header>
<h1>MASTER!!!!</h1>
</header>
<section layout:fragment="content">
<p>MASTER CODE</p>
</section>
<footer>
<p>My footer</p>
<p layout:fragment="custom-footer">Custom footer here</p>
</footer>
</body>
</html>
The index.html file
<p layout:decorator="layout" layout:fragment="content">
asada
</p>
the problem is that when I open the index.html, it doesn't include anything that it's in the layout.html file, files are next to other in root so no folders are in there, did I miss something in the configuration? thanks
Even using Spring Boot you have to include on your dependencies layout-dialect like this:
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
Use also the following dependency if you are using Spring Security:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
Version 3.0.0: Deprecated layout:decorator processor has been deleted
->layout:decorate (https://github.com/ultraq/thymeleaf-layout-dialect/blob/main/CHANGELOG.md
)
According to Read.md in https://github.com/ultraq/thymeleaf-layout-dialect you need to specify file, not file name. So you should have (if they are in the same directory):
<p layout:decorator="layout.html" layout:fragment="content">
asada
</p>
Moreover, Thymeleaf supports layouts that are also very usefull for including framents into code. More info can be found here: http://www.thymeleaf.org/doc/articles/layouts.html
I had a similar problem when i replace
templateEngine.setDialect(new LayoutDialect());
by
templateEngine.addDialect(new LayoutDialect());
this solve it