abc32112
abc32112

Reputation: 2527

NullPointerException for DAO

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...");
    }
}
...

Upvotes: 0

Views: 8671

Answers (2)

geoand
geoand

Reputation: 63991

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())

Upvotes: 3

Shoaib Chikate
Shoaib Chikate

Reputation: 8975

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;
}

Upvotes: -1

Related Questions