springjspspring-mvcdata-bindingpropertyeditor

NullPointerException in setAsText function in UserPropertyEditor (Spring 3.2)


ERROR

java.lang.NullPointerException
    at com.javalabs.web.dao.UserPropertyEditor.setAsText(UserPropertyEditor.java:20)
...

I can not bind the parameter from the form property userResponsible where I get the id of a user to bind to the object Task. But I'm getting the parameter userResponsible = 1, which is a user from database. Any idea from what can be?

form.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<h1>Create task</h1>
<form:form method="GET" class="form-horizontal"
    action="${pageContext.request.contextPath}/docreatetask"
    commandName="task">
...
    <div class="form-group">
        <label for="userresponsible" class="col-sm-2 control-label">User
            responsible</label>
        <div class="col-sm-10">
            <form:select path="userResponsible"  name="userResponsible" class="form-control">
                <form:option value="0" label="Select" />
                <form:options items="${users}" itemValue="idUser"
                    itemLabel="username" />
            </form:select>
            <div id="state.error">
                <span class="text-danger"><form:errors path="userResponsible" /></span>
            </div>
        </div>
    </div>
...

Task.java

@Entity
@Table(name = "t_task")
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "idTask")
    private long idTask;
    ...
    @ManyToOne
    @JoinColumn(name = "idUser_responsible", nullable = true)
    private User userResponsible;
    ...

UserPropertyEditor.java

public class UserPropertyEditor extends PropertyEditorSupport {

        private UserService userService;

        @Autowired
        public void setUserService(UserService userService) {
            this.userService = userService;
        }

        @Override
        public void setAsText(String text) throws IllegalArgumentException {
line 20:        super.setValue(userService.get(Long.parseLong(text)));
                //text variable is null when this function is called
            }
    }

TaskController.java

...
@InitBinder
public void initBinder(WebDataBinder binder, HttpServletRequest req) {

    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-mm-dd");
    dateFormat.setLenient(false);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    binder.registerCustomEditor(State.class, new StatePropertyEditor());
    binder.registerCustomEditor(Category.class, new CategoryPropertyEditor());
    binder.registerCustomEditor(Priority.class, new PriorityPropertyEditor());
    binder.registerCustomEditor(User.class, "userResponsible", new UserPropertyEditor());
    //binder.registerCustomEditor(User.class, new UserPropertyEditor());
}
...

What am I missing?


Solution

  • In your initBinder method you are constructing the UserPropertyEditor yourself, as this makes this a non spring managed bean the @Autowired doesn't do a thing.

    Either you have to call the setUserService yourself or you have to ask the application context to wire the instance for you.

    @Controller
    public class TaskController {
        @Autowired
        private UserService userService;
    
        @InitBinder
        public void initBinder(WebDataBinder binder, HttpServletRequest req) {
            UserPropertyEditor upe = new UserPropertyEditor();
            upe.setUserService(userService);
            binder.registerCustomEditor(User.class, "userResponsible", upe);
        }
    }
    

    When you call the setter yourself you have to inject the UserService into your controller so that you have access to it. Now construct a UserPropertyEditor and call the setUserService method on it.

    @Controller
    public class TaskController {
        @Autowired
        private ApplicationContext context;
    
        @InitBinder
        public void initBinder(WebDataBinder binder, HttpServletRequest req) {
            UserPropertyEditor upe = new UserPropertyEditor();
            context.getAutowireCapableBeanFactory().autowireBean(upe);
            binder.registerCustomEditor(User.class, "userResponsible", upe);
        }
    }
    

    When using the ApplicationContexct you have to inject that into your controller, get a AutowireCapableBeanFactory and call the autowireBean method on the freshly constructed instance of the bean. Now Spring will do any injection and callbacks for your object.