0

I'm getting a NullPointerException for userDao in this method. userDao is indeed null, but why and how do I fix it?

NOTE! Question has been updated based on suggestions from several people.

public class UserServiceImpl implements UserService {

private UserDao userDao;

@Override
public UserDao getUserDao() {
    return userDao; // userDao is null
}

@Autowired
@Override
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

@Override
public User add(User user) { 
    return getUserDao().insert(user);
}

@Override
public User get(String username) {
    return getUserDao().select(username);
}

@Override
public boolean userExists(String username) {
    return getUserDao().userExists(username); // throws NullPointerException (userDao is null -- confirmed with debugger)
}

springmvc-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<bean id="tweetService"
      class="com.springapp.mvc.tweet.TweetServiceImpl">
    <property name="tweetDao" ref="tweetDao"/>
</bean>

<bean id="tweetDao"
      class="com.springapp.mvc.tweet.TweetDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="userService"
      class="com.springapp.mvc.user.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao"
      class="com.springapp.mvc.user.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="followService"
      class="com.springapp.mvc.follow.FollowServiceImpl">
    <property name="followDao" ref="followDao"/>
</bean>

<bean id="followDao"
      class="com.springapp.mvc.follow.FollowDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="sessionFactory"
      class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mappingResources">
        <list>
            <value>User.hbm.xml</value>
            <value>Tweet.hbm.xml</value>
            <value>Follow.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="dialect">org.hibernate.dialect.H2Dialect</prop>
            <prop key="current_session_context_class">thread</prop>
            <prop key="hbm2ddl.auto">update</prop>
            <prop key="connection.pool_size">1</prop>
        </props>
    </property>
</bean>

<bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver"/>
    <property name="url" value="jdbc:h2:tcp://localhost/~/twitter"/>
    <property name="username" value=""/>
    <property name="password" value=""/>
</bean>

<bean id="dbUtil"
      class="com.springapp.mvc.util.DbUtil"
      init-method="initialize">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- register all beans except controllers -->
<context:component-scan base-package="com.springapp.mvc">
    <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>

</beans>

web.xml

 <web-app version="2.4"
     xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<display-name>Spring MVC Application</display-name>

<!-- intercepts requests -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<!-- protected URL path -->
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- location of Config -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:/applicationContext-security.xml
        classpath:/springmvc-config.xml
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- dispatches requests to controllers -->
<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- maps requests to path to mvc-dispatcher -->
<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>


</web-app>

mvc-dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

<context:component-scan base-package="com.springapp.mvc">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>

<mvc:annotation-driven/>

<mvc:resources mapping="/css/**" location="/css/"/>

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>

<mvc:resources mapping="/js/**" location="/js/**"/>
<mvc:resources mapping="/css/**" location="/css/**"/>
<mvc:resources mapping="/fonts/**" location="/fonts/**"/>

</beans>

applicationContext-security.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- security context -->
<beans:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:beans="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/security
         http://www.springframework.org/schema/security/spring-security.xsd">

    <http auto-config="true">
    <intercept-url pattern="/home/**" access="IS_AUTHENTICATED_FULLY"/>
    <intercept-url pattern="/users/**" access="IS_AUTHENTICATED_FULLY"/>
    <intercept-url pattern="/tweet/**" access="IS_AUTHENTICATED_FULLY"/>
    <form-login
            login-page="/login"
            default-target-url="/login?success"
            authentication-failure-url="/login?error"
            username-parameter="username"
            password-parameter="password"
            />
    <logout
            logout-url="/logout"
            logout-success-url="/"
            />
</http>

    <authentication-manager>
    <authentication-provider user-service-ref="userAccountDetailsService">
    </authentication-provider>
</authentication-manager>

<beans:bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

</beans:beans>

UserController.java

@Controller
public class UserController {

private static final Log logger = LogFactory.getLog(UserController.class);

@Autowired
private UserService userService;

@Autowired
private TweetService tweetService;

@Autowired
private FollowService followService;

@RequestMapping(value = {"/"})
public String landingPage() {
    logger.info("landingPage called");
    return "LandingPage";
}

@RequestMapping("/register")
public ModelAndView registerUser() {
    logger.info("registerUser called");
    ModelAndView model = new ModelAndView();
    User user = new User();
    model.addObject(user);
    model.setViewName("RegistrationForm");
    return model;
}

@RequestMapping(value = "/user_save", method = RequestMethod.POST)
public String saveUser(@ModelAttribute User user, BindingResult bindingResult) {
    logger.info("saveUser called");

    // validate form input
    UserValidator userValidator = new UserValidator();
    userValidator.validate(user, bindingResult); // NullPointerException occurs on this line, see validator below

    // if errors are found, return to registration form and display errors
    if (bindingResult.hasErrors()) {
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            logger.info("Code: " + fieldError.getCode() + ", field: " + fieldError.getField());
            return "RegistrationForm";
        }
    }

    // saveTweet user to database
    userService.add(user);
    return "LoginForm";
}
...

Validator method:

...
public void validate(Object target, Errors errors) {
    User user = (User) target;
    // confirm that required fields are filled in
    ValidationUtils.rejectIfEmpty(errors, "name", "name.required", "Error msg...");
    ValidationUtils.rejectIfEmpty(errors, "username", "username.required", "Error msg...");
    ValidationUtils.rejectIfEmpty(errors, "password", "password.required", "Error msg...");
    // if the user has filled in a username, check if if already exists
    String username = user.getUsername();
    UserService userService = new UserServiceImpl();
    if (username != null && userService.userExists(username)) { // this calls the userExists() method that causes a NullPointerException
        errors.rejectValue("username", "username.exists", "Error msg...");
    }
}
...
abc32112
  • 2,457
  • 9
  • 37
  • 53
  • `@Autowired` is missing over the `userDao` attribute. – sp00m Aug 13 '14 at 13:27
  • I've tried adding @Autowired, but it didn't change anything. :/ – abc32112 Aug 13 '14 at 13:28
  • I can only assume that somewhere in your .xml you have a `component-scan` or similar and you are using in your code that instance of the `UserServiceImpl` that was created by `component-scan` and not by the .xml definition. So, basically, you have two instances of `UserServiceImpl` but using the "wrong" one. – Andrei Stefan Aug 13 '14 at 13:29
  • I did and it was indeed picking up this class, but I narrowed it down to directories excluding UserServiceImpl. Still the same... – abc32112 Aug 13 '14 at 13:37
  • Do you have any other Spring configuration files? – geoand Aug 13 '14 at 13:38
  • Then you need to provide some more code because what you posted shows a valid configuration. – Andrei Stefan Aug 13 '14 at 13:38
  • Edited to add the rest of the config files. – abc32112 Aug 13 '14 at 13:44
  • @CaptainHindsight Any execeptions in your logfile? – Jens Aug 13 '14 at 13:45
  • 1
    MVC stuff is usually defined under DispatcherServlet? Any reason for your choice? Also, do you have a `mvc-dispatcher-servlet.xml` file? – Andrei Stefan Aug 13 '14 at 13:45
  • I'm writing this project to learn Spring, so I suppose the reason is "I'm figuring this out". :) – abc32112 Aug 13 '14 at 13:46
  • added dispatcher servlet – abc32112 Aug 13 '14 at 13:47
  • 1
    You have enabled `component-scan` in `mvc-dispatcher-servlet.xml`. You should change that to scan only the packages where controllers reside. – geoand Aug 13 '14 at 13:50
  • You are right, I missed that. However, I removed it and the error remains. :/ – abc32112 Aug 13 '14 at 13:53
  • What did you change it too? – geoand Aug 13 '14 at 13:54
  • 1
    Oh, ok then :-). The simplest test you can do: remove `springmvc-config.xml` from `contextConfigLocation`. Move everything under `springmvc-config.xml` to `mvc-dispatcher-servlet.xml`. From all the `component-scan`s in the resulting `mvc-dispatcher-servlet.xml` keep only that one that refers to controllers. Give it another try. – Andrei Stefan Aug 13 '14 at 13:54
  • I have two, one set to base-package="com.springapp.mvc.scan" (temporarily) and the other com.springapp.mvc.controller. UserServiceImpl resides in com.springapp.mvc.user. – abc32112 Aug 13 '14 at 13:56
  • @CaptainHindsight Can you update the code with how you are obtaining a reference to the `UserServiceImpl` bean? Thanks – geoand Aug 13 '14 at 14:36
  • @geoand Done, You'll find the controller and validater at the very bottom. – abc32112 Aug 13 '14 at 14:46
  • 1
    `UserService userService = new UserServiceImpl();` is a no-no :-). – Andrei Stefan Aug 13 '14 at 14:47
  • That is indeed it, help much appreciated! – abc32112 Aug 13 '14 at 14:59

2 Answers2

3

You have enabled component-scan in mvc-dispatcher-servlet.xml. That causes your UserServiceImpl bean to be registered in two different contexts. The root application context (where it's registered because of the explicit XML configuration), and the web application context (where it's registed via component scanning).

(Check out this to see more on the difference between the two contexts)

The usual practice is to register all beans besides controllers in the root application context (in your case via springmvc-config.xml). The controllers would then be registered in the web application context (in your case via mvc-dispatcher-servlet.xml).

So in your case you change component scanning in springmvc-config.xml to

<context:component-scan base-package="com.springapp.mvc">
    <context:exclude-filter expression="org.springframework.stereotype.Controller"
        type="annotation"/>
</context:component-scan>

and in mvc-dispatcher-servlet.xml

<context:component-scan base-package="com.springapp.mvc">
    <context:include-filter expression="org.springframework.stereotype.Controller"
        type="annotation"/>
</context:component-scan>.

The former component scan forces the Spring to pick up all classes (under com.springapp.mvc) annotated with any stereotype annotation except @Controller, while the latter one only includes @Controller annotated classes.

Also you have the stereotype @Service on UserServiceImpl. Remove it since you are explicitly configuring the bean in XML and the class should not be picked up by component scanning.

On a separate note, the configuration

<mvc:annotation-driven/>
<mvc:resources mapping="/css/**" location="/css/"/>

belongs in mvc-dispatcher-servlet.xml, not in springmvc-config.xml.

UPDATE

You are obtaining the UserServiceImpl using

UserService userService = new UserServiceImpl();

That mean that you are not using Spring to obtain it. You need to remove that and use the reference obtained from

@Autowired
private UserService userService

in the UserValidator. That of course would mean that the UserValidator has to be a Spring bean too.

So you have

@Component
public class UserValidator {

   @Autowired
   private UserService userService;

    public void validate(Object target, Errors errors) {
      User user = (User) target;
      // confirm that required fields are filled in
      ValidationUtils.rejectIfEmpty(errors, "name", "name.required", "Error msg...");
      ValidationUtils.rejectIfEmpty(errors, "username", "username.required", "Error msg...");
      ValidationUtils.rejectIfEmpty(errors, "password", "password.required", "Error msg...");
      // if the user has filled in a username, check if if already exists
      String username = user.getUsername();
      if (username != null && userService.userExists(username)) { // this calls the userExists() method that causes a NullPointerException
        errors.rejectValue("username", "username.exists", "Error msg...");
      }
   }

}

And you would need to add

@Autowired
private UserValidator userValidator

in the UserController (while also removing UserValidator userValidator = new UserValidator())

Community
  • 1
  • 1
geoand
  • 60,071
  • 24
  • 172
  • 190
  • Your thorough response is much appreciated, probably improved my code and possibly resolved some future problems with several component scans. However, this particular problem remains. I'll update my code in the main question to reflect changes. – abc32112 Aug 13 '14 at 14:24
  • @CaptainHindsight Ok, I'll check the updated question – geoand Aug 13 '14 at 14:26
  • @CaptainHindsight You should probably explicitly mention that you havr updated the question to help future viewers – geoand Aug 13 '14 at 14:29
  • @CaptainHindsight, you don't need `@Autowired` in the service class for `setUserDao`. The injection is done in .xml. Also, where are you using this service class? – Andrei Stefan Aug 13 '14 at 14:36
  • The class is called from the controller. I'll add the code. – abc32112 Aug 13 '14 at 14:37
  • Added code (at the very bottom) for controller, which calls validator, which call, userExists, which causes a NullPointerException. – abc32112 Aug 13 '14 at 14:45
  • @CaptainHindsight It should be working now with the latest update. I hadn't seen all the code changes you posted – geoand Aug 13 '14 at 14:54
  • Oh wow, this works now. I'm impressed by Spring but it's not easy to wrap your head around in a short amount of time. Thank you very much for your help, it's much appreciated! – abc32112 Aug 13 '14 at 14:58
  • Glad that it helped! Spring is downright awesome, but it takes a while to get used to. – geoand Aug 13 '14 at 14:59
-1

Here in this case your object doesn't get injected and hence it is null.

You can use @Autowired annotation and you can enabled it by using component-scan of context namespace

<context:component-scan  base-package="XXX"/>

Or define setter methods as I can see that you have given property in bean defination so you can give setter method for DAO which need to be injected.

xml file:

<bean id="userService"
      class="com.springapp.mvc.user.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>

UserServiceImpl

public void setUserDAO(UserDAO userDAO){
    this.userDAO=userDAO;
}
Shoaib Chikate
  • 8,665
  • 12
  • 47
  • 70