enter image description here> Been stuck on this for a few days. I believe that the issue has to do
with the class itself. Or the relationship between the class in question and the class its column is joined on.
Here are my classes: Mod
package com.matt.Keyword.models;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
@Table(name = "Mods")
public class Mod {
@Id
@GeneratedValue
private int id;
@NotNull
private Integer role;
@NotNull
@Size(min = 1, message = "An entry is required")
private String entry;
@Column(name = "user_id")
private Integer userid;
public Mod(Integer role, String entry) {
this.role = role;
this.entry = entry;
}
public Mod() {
}
public int getId() {
return id;
}
public int getRole() {
return role;
}
public void setRole(Integer role) {
this.role = role;
}
public String getEntry() {
return entry;
}
public void setEntry(String entry) {
this.entry = entry;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid= userid;
}
}
Account
package com.matt.Keyword.models;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
public class Account {
@Id
@GeneratedValue
private int id;
@NotNull
@Size(min = 3, message = "Try again")
private String name;
@NotNull
@Size(min = 1, message = "A password is required")
private String password;
@Column(name = "user_id")
private Integer userid;
public Account(String name, String password) {
this.name = name;
this.password = password;
}
public Account() {
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
}
Account and Mod classes are very similar and share a relationship with the user_id column in the user class.
User
package com.matt.Keyword.models;
import javax.persistence.*;
import javax.validation.constraints.Size;
import java.util.List;
@Entity
public class User {
@Id
@GeneratedValue
@Column(name = "user_id")
private int id;
@Size(min=3, message = "Try again")
private String email;
@Size(min=1, message = "A password is required")
private String password;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "user_id")
private List<Account> accounts;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "user_id", referencedColumnName = "user_id")
private List<Mod> mods;
public User(String email, String password) {
this.email = email;
this.password = password;
}
public User() {}
public int getId() {
return id;
}
public void setId(Integer id){
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public List<Mod> getMods() {
return mods;
}
public void setMods(List<Mod> mods) {
this.mods = mods;
}
@Override
public String toString()
{
return email + " " + password;
}
}
The exception gets thrown when I load the index page from my mods controller. I am using thymeleaf to render views. Here is my mods controller. The Mods database has objects saved to it and in this controller during debugging the attribute being passed to the view contains a populated list of Mod objects. ModController
package com.matt.Keyword.controllers;
import com.matt.Keyword.models.Mod;
import com.matt.Keyword.models.User;
import com.matt.Keyword.models.data.ModDao;
import com.matt.Keyword.models.data.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
//test commit note
//second try
//third try
/**
* Created by LaunchCode
*/
@Controller
@RequestMapping("mod")
public class ModController {
@Autowired
private UserDao userDao;
@Autowired
private ModDao modDao;
@RequestMapping(value = "")
public String index(Model model, HttpSession session) {
if (session.getAttribute("currentUser") == null) {
return "redirect:/keyword/login";
}
session.getAttribute("currentUser");
model.addAttribute("title", "Mods");
model.addAttribute("mods", modDao.findAll());
return "mods/index";
}
@RequestMapping(value = "add", method = RequestMethod.GET)
public String displayModForm(Model model, HttpSession session) {
if (session.getAttribute("currentUser") == null) {
return "redirect:/keyword/login";
}
model.addAttribute("title", "Add a new mod");
model.addAttribute(new Mod());
return "mods/add";
}
@RequestMapping(value = "add", method = RequestMethod.POST)
public String processAddModForm(@ModelAttribute @Valid Mod newMod,
Errors errors, Model model, HttpSession session) {
if (errors.hasErrors()) {
model.addAttribute("title", "List of Mods");
return "mods/add";
}
User currentUser = (User) session.getAttribute("currentUser");
session.getId();
currentUser.setId(currentUser.getId());
newMod.setUserid(currentUser.getId());
modDao.save(newMod);
return "redirect:";
}
}
Here is the mod/index page using thymeleaf.
mod/index
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Default Title</h1>
<p th:unless="${mods} and ${mods.size()}">No current mods</p>
<nav th:replace="fragments :: navigation"></nav>
<p th:text="${session.currentUser.email}"></p>
<table class="table">
<tr>
<th>Type</th>
<th>Entry</th>
</tr>
<div th:if="${mods} != null">
<tr th:each="mod : ${mods}">
<div th:if="${mod.userid} == ${session.currentUser.id}">
<td th:text="${mod.role}"></td>
<td th:text="${mod.entry}"></td>
</div>
</tr>
</div>
</table>
</body>
</html>
The exception seems to be associated with the for th:each statement. Particularly with the "mod" iterator variable. Here is the stack trace for the full error: Trace
There was an unexpected error (type=Internal Server Error, status=500).
An error happened during template parsing (template: "class path resource [templates/mods/index.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/mods/index.html]")
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241)
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072)
at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:362)
at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:189)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1370)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1116)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.attoparser.ParseException: Error during execution of processor 'org.thymeleaf.standard.processor.StandardEachTagProcessor' (template: "mods/index" - line 21, col 17)
at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
... 52 more
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.standard.processor.StandardEachTagProcessor' (template: "mods/index" - line 21, col 17)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117)
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304)
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278)
at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleOpenElementEnd(OutputExpressionInlinePreProcessorHandler.java:186)
at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:124)
at org.attoparser.HtmlElement.handleOpenElementEnd(HtmlElement.java:109)
at org.attoparser.HtmlMarkupHandler.handleOpenElementEnd(HtmlMarkupHandler.java:297)
at org.attoparser.MarkupEventProcessorHandler.handleOpenElementEnd(MarkupEventProcessorHandler.java:402)
at org.attoparser.ParsingElementMarkupUtil.parseOpenElement(ParsingElementMarkupUtil.java:159)
at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:710)
at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301)
... 54 more
Caused by: java.lang.IllegalArgumentException: Iteration variable cannot be null
at org.thymeleaf.util.Validate.notNull(Validate.java:37)
at org.thymeleaf.standard.expression.Each.<init>(Each.java:49)
at org.thymeleaf.standard.expression.EachUtils.composeEach(EachUtils.java:169)
at org.thymeleaf.standard.expression.EachUtils.internalParseEach(EachUtils.java:94)
at org.thymeleaf.standard.expression.EachUtils.parseEach(EachUtils.java:65)
at org.thymeleaf.standard.processor.StandardEachTagProcessor.doProcess(StandardEachTagProcessor.java:59)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
... 67 more
Any help is appreciated. Thanks in advance for just taking a look if you have the time.
Debugger in mod controller Here is a to link to my repo if you want to download it and give it a run.
This issue turned out to be quite a tricky one. The story of its resolution is as follows ...
Judging by the stacktrace:
Caused by: java.lang.IllegalArgumentException: Iteration variable cannot be null
there was an iteration in a thymeleaf template that had a null
variable being iterated over. The only use of th:each
I could find was:
<div th:if="${mods} != null">
<tr th:each="mod : ${mods}">
...
</tr>
</div>
So it seemed that mods
was null
and hence th:each="mod : ${mods}"
is the where the issue lies.
However there was a check for this in place with <div th:if="${mods} != null">
. Or was there? Would the following be more useful:
<div th:if="${mods != null}">
ie. moving the }
so that the null
check is within the expression.
That didn't help, so the result of modDao.findAll()
was checked and there were two elements found. So it had to be something else.
Reviewing the basics of iteration in Thymeleaf suggested that perhaps the collection being returned by findAll()
was not one of supported types that th:each
can iterate over.
So this collection was copied into an Iterable
and given a different name and the problem was solved.
Interestingly, if the iteration variable was given the name mod
the problem still occurred. Turns out mod
is a keyword in Thymeleaf and is use for the modulo operation (%
).
Ultimately, there were two issues with apparently correct code:
th:each
; andmod
is a keyword in Thymeleaf and shouldn't be used as a variable name.