How to get Dynamic Drop Down Box in JSP - spring

I want to populate a dropdown box in Spring MVC i tried to do this but i am getting null pointer exception
this is my controller:
public class CatalogueController {
private CatalogueService catalogueService;
private PublisherService publisherService;
private SubjectService subjectService;
// some code and i generated setter and getter methods
............
#RequestMapping(value="/catalogue/new.action", method=RequestMethod.GET)
public ModelAndView newMember() throws Exception {
ModelAndView mvc= null;
mvc = new ModelAndView("catalogue/catalogueForm", "catalogueForm", new CatalogueBase());
mvc.addObject("copyDetailForm", new CatalogueCopyDetails());
List<Publisher> publist = publisherService.getPublisherList();
mvc.addObject("publist", publist);
List<Subject> subjectlist = subjectService.getSubjectList();
mvc.addObject("subjectlist", subjectlist);
return mvc;
}
this is my service method :
#Override
public List<Publisher> getPublisherList() {
List<Publisher> list = publisherDAO.getPublisher();
return list;
}
#Override
public List<Subject> getSubjectList() {
List<Subject> list = subjectDAO.getSubjects();
return list;
}
this is my DAO Method:
#SuppressWarnings("unchecked")
public List<Publisher> getPublisher() {
Query qry = getSession().createQuery("from Publisher");
return qry.list();
}
#SuppressWarnings("unchecked")
public List<Subject> getSubjects() {
Query qry = getSession().createQuery("from Subject");
return qry.list();
}
finally this is my JSP page :
<form:form commandname="catalogueForm" action="${pageContext.request.contextPath}/catalogue/create.action" method="post" modelAttribute="catalogueForm">
<form:select path="publisher.id" id="publisher.id">
<form:options items="${publist}" itemValue="id" itemLabel="name" />
</form:select>
<form:select path="subject.id" id="subject.id">
<form:options items="${subjectlist}" itemValue="id" itemLabel="name" />
</form:select>
</form:form>
This is Stack Trace:
java.lang.NullPointerException
at com.easylib.elibrary.webapp.controller.catalogue.CatalogueController.newMember(CatalogueController.java:80)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:100)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:604)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:565)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)

You are getting the npe at line 80 of your controller, but I can't see which line that is in your question.
My guess is that you haven't wired in your services correctly and either "publisherService" or "subjectService" is null.

Related

Sping MVC and hibernate: undeclared variable in jsp become null when saved

I just created my simple web application using Spring frameworks and hibernate. But I face the crucial problem when there is a transaction with my database.
When I tried to update my object, undeclared variables become null when saved to the database.
I will explain it with an example.
Model.java:
public class Model implements Serializable {
private int id;
private String firstName;
private String lastName;
private String description;
//setter getter
}
ModelDaoImpl.java:
#Repository("modelDao")
public class ModelDaoImpl implements ModelDao {
#Autowired
SessionFactory sessionFactory;
public Model create (Model model) throws Exception {
this.sessionFactory.getCurrentSession().save(model);
return model;
}
public Model update (Model model) throws Exception {
this.sessionFactory.getCurrentSession().update(model);
return model;
}
public Model get (Serializable id) throws Exception {
return (Model) this.sessionFactory.getCurrentSession().get(Model.class, id);
}
}
ModelServiceImpl.java:
#Service("modelService")
public class ModelServiceImpl implements ModelService {
#Autowired
modelDao modelDao;
#Transactional
public Model create (Model model) throws Exception {
return modelDao.create(model);
}
#Transactional
public Model update (Model model) throws Exception {
return modelDao.udpdate(model);
}
#Transactional
public Model get (Serializable id) throws Exception {
return modelDao.get(id);
}
}
ModelController.java:
#Controller
public class ModelController {
#Autowired
ModelService modelService
#RequestMapping(value="/editModel", method.RequestMethod.GET)
public String formCreator(ModelMap model, HttpServletRequest request) {
Integer id = new Integer(request.getParameter("id"));
Model modelData = new Model();
if (id != null) {
modelData = modelService.get(id);
}
model.addAttribute("modelData", modelData);
return "editModel"
}
#RequestMapping(value="/saveModel", method.RequestMethod.POST)
public String saveData(#ModelAttribute("modelData") Model modelData) {
if (modelData.getId() == null) {
modelService.create(modelData);
} else {
modelService.update(modelData);
}
return "redirect:/modelList";
}
//SKIP FOR GET MODEL LIST AND GET MODEL DETAIL
}
editModel.jsp:
<form:form action="${pageContext.request.contextPath}/saveModel" method="POST" modelAttribute="modelData">
<table>
<form:hidden path="id"/>
<tr>
<td>First Name:</td>
<td><form:input path="firstName"/></td>
</tr>
<tr>
<td>Last Name:</td>
<td><form:input path="lastName"/></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="Save"></td>
</tr>
</table>
</form:form>
From jsp page we see that I didn't declare variable 'description', but when update my existing data the variable 'description' become null. I have tried using #DynamicUpdate or related to dynamic update process, but the result was still the same, the variable 'description' still become null.
Any suggestion?
The problem is that the way your controller is written: a new Model instance is created
and populated on execution of saveData.
You can update your code to be like the below. Now on POST the Model instance
will be that returned from getModel() rather than a new instance and only the
values specified in the incoming request wil be updated. We can also update the GET to use the same code to populate the model.
Essentially then:
on GET - the model will be populated for the UI by a call to #ModelAttribute
on POST - the model will be populated and the Model instance returned will be passed to saveData();
See:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-modelattrib-method-args
#Controller
public class ModelController {
#Autowired
ModelService modelService
#RequestMapping(value="/editModel", method.RequestMethod.GET)
public String formCreator(HttpServletRequest request) {
return "editModel"
}
#RequestMapping(value="/saveModel", method.RequestMethod.POST)
public String saveData(#ModelAttribute("modelData") Model model) {
//you can have 1 method in your service and DAO
//DAO can use saveOrUpdate() method of session;
modelService.persist(model);
return "redirect:/modelList";
}
#ModelAttribute("modelData")
public Model getModel(#RequestParam(name = "id", required = false)Integer id){
return id != null ? modelService.get(id) : new Model();
}
}

How to bind Spring form:checkbox instead of form:checkboxes?

I am having problems with form:checkbox. I cannot make it display selected values. When I selected values and submit, correct values are display in database. When I load page all values (checkboxes) are not selected.
Elements below are located inside this:
<form:form role="form" commandName="user" class="form-horizontal" action="${form_url}">
</form:form>
This works just fine:
<form:checkboxes items="${availableRoles}" path="roles" itemLabel="role" itemValue="id" element="div class='checkbox'"/>
This doesn't work:
<c:forEach items="${availableRoles}" var="r" varStatus="status">
<div class="checkbox">
<form:checkbox path="roles" label="${r.description}" value="${r.id}"/>
</div>
</c:forEach>
This is my domain class:
public class User {
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
This is my custom property editor:
public class RolePropertyEditor extends PropertyEditorSupport {
#Override
public void setAsText(String text) {
Role role = new Role();
role.setId(Integer.valueOf(text));
setValue(role);
}
}
Controller has this method:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Role.class, new RolePropertyEditor());
}
Controller method:
#RequestMapping(value = "/update/{userId}", method = RequestMethod.GET)
public String updateUser(#PathVariable Integer userId, Model model) {
User user = userService.getByUserId(userId);
List<Role> availableRoles = roleService.getAllRoles();
model.addAttribute("availableRoles", availableRoles);
model.addAttribute("user", user);
return "user/update";
}
After debugging session I found the solution.
Because of Spring internals JSP should look like this:
<c:forEach items="${availableRoles}" var="r">
<div class="checkbox">
<form:checkbox path="roles" label="${r.description}" value="${r}" />
</div>
</c:forEach>
Notice that value is item (r), not item's member like r.id.
Also you need getAsText implementation in your custom PropertyEditor.
#Override
public String getAsText() {
Role role = (Role) this.getValue();
return role.getId().toString();
}

Spring - Model attributes after BindingResult with errors

In the following piece of code I just want to create a new user and link it to the selected groups.
Everything works fine when the user and group are valid. The problem comes when the bindingresult has errors. The controller detects such error (all fine so far) and returns the same view (I want to keep the data entered by the user) but the list of groups is empty (I have discovered that, after showing again the view, userform.groups is null).
Has anyone a clue about what the problem could be?
UserForm
#Component
public class UserForm {
#Valid
private User user;
#Valid
private Collection<Group> allGroups;
// Setters and getters
}
UserController
#Controller
public class UserController {
#Autowired
UserGroupService userGroupService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public ModelAndView createUsetGet () {
ModelAndView mav = new ModelAndView("/admin/users/CreateUser");
UserForm userForm = new UserForm();
userForm.setUser(new User());
userForm.setGroups(userGroupService.getAllEnabledGroups());
mav.addObject("userform", userForm);
return mav;
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
}
CreateUser.jsp (Only piece regarding the groups)
<form:form modelAttribute="userform" method="post">
Username:
<form:input path="user.loginName"/>
<!-- More fields -->
<form:select path="user.groups" multiple="true">
<form:options items="${userform.groups}" itemValue="id" itemLabel="name" />
</form:select>
<button type="submit">Create</button>
</form:form>
Any help is appreciated!
The object gets recreated and values are bound to the resulting object. Which means no group objects.
Also those shouldn't be in the object at all. To solve use a #ModelAttribute annotated method, which will be invoked for each request handling method and create an object and fill the list of groups.
#ModelAttribute
public void init(Model model) {
UserForm userForm = new UserForm();
userForm.setUser(new User());
model.addAttribute("userform", userForm);
model.addAtrribute("groups", userGroupService.getAllEnabledGroups());
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
return "redirect:/admin/users/ViewUsers";
}
Ofcourse your jsp has to change slightly also.
<form:select path="user.groups" multiple="true">
<form:options items="${groups}" itemValue="id" itemLabel="name" />
</form:select>
There is one drawback of using this approach now the userGroupService.getAllEnabledGroups() is called for each incoming request. This might not be needed. You could store those in the session using the #SessionAttributes annotation on the class.
#Controller
#SessionAttributes("groups")
public class UserController {
#Autowired
UserGroupService userGroupService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#InitBinder
public void initBinder (WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "userform.user.groups", new GroupListEditor(userGroupService));
}
#ModelAttribute("groups")
public List<Group> groups() {
return userGroupService.getAllEnabledGroups();
}
#ModelAttribute("userform")
public UserForm userform() {
UserForm userForm = new UserForm();
userForm.setUser(new User());
return userForm;
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.GET)
public String createUsetGet () {
return "/admin/users/CreateUser";
}
#RequestMapping(value = "/admin/users/CreateUser", method = RequestMethod.POST)
public String createUserPost (#Valid #ModelAttribute("userform") UserForm userForm, BindingResult result, SessionStatus status) {
if (result.hasErrors() == true) {
return "/admin/users/CreateUser";
}
userForm.getUser().setPassword(passwordEncoder.encode(userForm.getUser().getPassword()));
userGroupService.saveUser(userForm.getUser());
status.setComplete();
return "redirect:/admin/users/ViewUsers";
}
}
You will then need, on success, to tell the SessionStatus that you are finished. If you don't do this your session might pollute.
It's because the information about the validation errors is lost after redirect.
You can solve this using RedirectAttributes. Check this tutorial.

Can I add any object to Model in Spring MVC?

I wanted to add a bean to model in Spring MVC Controller. But, validator exception is thrown:
Illegalstate exception.
Can anyone guide me to submit a form and display the content which I get after form submission? In this case, I need to use a bean to display all my information in view.
Like:
model.addAttribute("simple", new Student());
But, I am keep getting IllegaStateException from validator.
Download:
https://sites.google.com/site/jimjicky/SpringFormValidation.rar?attredirects=0&d=1
Controller:
#Controller
public class EmployeeController {
private static final Logger logger = LoggerFactory
.getLogger(EmployeeController.class);
private Map<Integer, Employee> emps = null;
#Autowired
#Qualifier("employeeValidator")
private Validator validator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
public EmployeeController() {
emps = new HashMap<Integer, Employee>();
}
#ModelAttribute("employee")
public Employee createEmployeeModel() {
// ModelAttribute value should be same as used in the empSave.jsp
return new Employee();
}
#ModelAttribute("student")
public Student createStudentModel() {
// ModelAttribute value should be same as used in the empSave.jsp
return new Student();
}
#RequestMapping(value = "/emp/save", method = RequestMethod.GET)
public String saveEmployeePage(Model model) {
logger.info("Returning empSave.jsp page");
return "empSave";
}
#RequestMapping(value = "/emp/save.do", method = RequestMethod.POST)
public String saveEmployeeAction(
#ModelAttribute("employee") #Validated Employee employee,#ModelAttribute("student")Student student,
BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
logger.info("Returning empSave.jsp page");
return "empSave";
}
logger.info("Returning empSaveSuccess.jsp page");
model.addAttribute("emp", employee);
model.addAttribute("student", createStudentModel());
emps.put(employee.getId(), employee);
return "empSaveSuccess";
}
}
Validator:
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.journaldev.spring.form.model.Employee;
public class EmployeeFormValidator implements Validator {
//which objects can be validated by this validator
#Override
public boolean supports(Class<?> paramClass) {
return Employee.class.equals(paramClass);
}
#Override
public void validate(Object obj, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "id.required");
Employee emp = (Employee) obj;
if(emp.getId() <=0){
errors.rejectValue("id", "negativeValue", new Object[]{"'id'"}, "id can't be negative");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "role", "role.required");
}
}
Stack Trace
java.lang.IllegalStateException: Invalid target for Validator [com.journaldev.spring.form.validator.EmployeeFormValidator#1625cd8]: com.journaldev.spring.form.model.Student#bd8550
org.springframework.validation.DataBinder.assertValidators(DataBinder.java:495)
org.springframework.validation.DataBinder.setValidator(DataBinder.java:486)
com.journaldev.spring.form.controllers.EmployeeController.initBinder(EmployeeController.java:38)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.method.annotation.InitBinderDataBinderFactory.initBinder(InitBinderDataBinderFactory.java:62)
org.springframework.web.bind.support.DefaultDataBinderFactory.createBinder(DefaultDataBinderFactory.java:53)
org.springframework.web.method.annotation.ModelFactory.updateBindingResult(ModelFactory.java:222)
org.springframework.web.method.annotation.ModelFactory.updateModel(ModelFactory.java:206)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.getModelAndView(RequestMappingHandlerAdapter.java:852)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:755)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
Based on the information provided try changing your InitBinder to following:
#InitBinder("employee")
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
The reason this works is because of the value provided, in this case the value being "employee".
public abstract String[] value
The names of command/form attributes and/or request parameters that this init-binder method is supposed to apply to.
In your form you have multiple objects, without advising the #InitBinder what object to validate it attempted to validate your Student object as well, thus failing as it didn't meet the class requirements.
By specifying "employee" it basically ensured it only applied the validation against the Employee object.

Exception while implementing LazyModel for primefaces datalist

I am trying to implement the lazy loading model for the primefaces datalist similar to datatable as shown here.
My initial code with the normal AJAX pagination feature works absolutely fine. However, when I try using the lazy loading model, I get the exception below when the page loads :
com.sun.faces.application.view.FaceletViewHandlingStrategy handleRenderException
SEVERE: Error Rendering View[/pages/index.xhtml]
java.io.NotSerializableException: java.util.ArrayList$SubList
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1180)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at java.util.HashMap.writeObject(HashMap.java:1100)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:975)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1480)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1528)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1493)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1362)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1170)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at java.util.HashMap.writeObject(HashMap.java:1100)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:975)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1480)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1416)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at com.sun.faces.renderkit.ClientSideStateHelper.doWriteState(ClientSideStateHelper.java:325)
at com.sun.faces.renderkit.ClientSideStateHelper.writeState(ClientSideStateHelper.java:173)
at com.sun.faces.renderkit.ResponseStateManagerImpl.writeState(ResponseStateManagerImpl.java:122)
at com.sun.faces.application.StateManagerImpl.writeState(StateManagerImpl.java:166)
at com.sun.faces.application.view.WriteBehindStateWriter.flushToWriter(WriteBehindStateWriter.java:225)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:418)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
Here's the code of index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<ui:composition template="/pages/templates/template.xhtml">
<ui:define name="content">
<h:form prependId="false" id="form">
<p:dataList value="#{movies.lazyMovieModel}" var="movie" id="movies" paginator="true" rows="10"
paginatorTemplate="{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}"
type="none" paginatorAlwaysVisible="false" lazy="true">
<h:outputText value="#{movie.movieName}, #{movie.releaseYear}" style="margin-left:10px">
</h:outputText>
<br/>
</p:dataList>
</h:form>
</ui:define>
</ui:composition>
</html>
MovieListBean.java
import org.primefaces.model.LazyDataModel;
import com.clixflix.enitities.Movie;
import com.clixflix.jsf.extensions.LazyMovieDataModel;
#ManagedBean(name = "movies")
#ViewScoped
public class MovieListBean extends BaseBean implements Serializable
{
private static final long serialVersionUID = -5719443344065177588L;
private LazyDataModel<Movie> lazyMovieModel;
#PostConstruct
public void initialize() {
lazyMovieModel = new LazyMovieDataModel();
}
public LazyDataModel<Movie> getLazyMovieModel()
{
List<Movie> movieList = getServiceLocator().getMovieService().getMovieList();
((LazyMovieDataModel) lazyMovieModel).setMovieList(movieList);
return lazyMovieModel;
}
}
LazyMovieDataModel.java (LazyModel implementation)
public class LazyMovieDataModel extends LazyDataModel<Movie>
{
private static final long serialVersionUID = 8745562148994455749L;
private List<Movie> movieList;
public LazyMovieDataModel() {
this.movieList = Collections.emptyList();
}
#Override
public List<Movie> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
// Sorting
if (null != sortField) {
LazySorter sorter = new LazySorter(sortField, sortOrder);
Collections.sort(movieList, sorter);
sorter = null;
}
// RowCount
int rowCount = movieList.size();
this.setRowCount(rowCount);
// Pagination
if (rowCount > pageSize) {
return movieList.subList(first, (first + pageSize));
}
else {
return movieList;
}
}
private class LazySorter implements Comparator<Movie>
{
private String sortField;
private SortOrder sortOrder;
LazySorter(String sortField, SortOrder sortOrder) {
this.sortField = sortField;
this.sortOrder = sortOrder;
}
#SuppressWarnings("unchecked")
#Override
public int compare(Movie movie1, Movie movie2) {
Object value1 = null, value2 = null;
try {
value1 = Movie.class.getField(this.sortField).get(movie1);
value2 = Movie.class.getField(this.sortField).get(movie2);
int value = ((Comparable<Object>) value1).compareTo(value2);
return SortOrder.ASCENDING.equals(sortOrder) ? value : -1 * value;
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
e.printStackTrace();
return 0;
}
}
}
public void setMovieList(List<Movie> movieList) {
this.movieList = movieList;
}
}
I am assuming the exception is at this line:
return movieList.subList(first, (first + pageSize));
Could anyone please guide me as to what am I missing?
Also, I observe in the logs, that when I use the lazymodel, the DB gets queried three times but when I use the normal AJAX pagination, the DB is queried only once :|
UPDATE: I figured out the reason for the DB being queried 3 times. It was because I was calling my service in the getter of the LazyModel instead of only in the load method.
I made the following changes in the classes:
LazyMovieDataModel.java
public class LazyMovieDataModel extends LazyDataModel<Movie>
{
private static final long serialVersionUID = 8745562148994455749L;
public LazyMovieDataModel() {}
#Override
public List<Movie> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
List<Movie> movieList = getServiceLocator().getMovieService().getMovieList(first, (first + pageSize));
// RowCount
int rowCount = ((Number)getServiceLocator().getMovieService().getMovieCount()).intValue();
this.setRowCount(rowCount);
}
}
LazyModel getter in MovieListBean.java
/* Removed PostConstruct init method */
public LazyDataModel<Movie> getLazyMovieModel()
{
return lazyMovieModel;
}
The above changes work fine on the initial page load. However, when I hit the next page button (or any pagination button) I get an NPE for getServiceLocator() in the load method.
The serviceLocator is a protected access modified managed property inherited from BaseBean and injected using Spring.
Any reason why the getter returns null on subsequent invokes ???
ArrayList$SubList is a problem. The subList returned does not implement serializable.
Try using:
return new ArrayList(movieList.subList(first, (first + pageSize)));
Same problem in this link.There are 2 load methods in LazyDataModel:
public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String,String> filters) {
throw new UnsupportedOperationException("Lazy loading is not implemented.");
}
public List<T> load(int first, int pageSize, List<SortMeta> multiSortMeta,Map<String,String> filters) {
throw new UnsupportedOperationException("Lazy loading is not implemented.");
}
This is where the error is thrown. You are using multisort, so you should override the second.Default method is the first one.

Resources