Hello guys,
I am new to Spring and currently I am working on a web project in which I use Spring MVC and Spring dependency injection. I have three classes. UserService, HomeController and User. In HomeController I invoke UserService's fetchUser method which returns me a new Instance of the User class. It is a simple logic I use to reproduce my real project problem. I am not using any database in this simple project. When I invoke the fetchUserMethod I am getting a NullPointerException. I am using Glassfish 4.0. Could you please help me ?
Thank you !
Here is my web.xml
`<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/aplicationContext.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>`
Here is my applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean id="userService" class="services.UserService"/>
<bean id="homeController" class="controllers.HomeController">
<property name="userService" ref="userService"/>
</bean>
</beans>
Here is my servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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">
<!-- Enables the Spring MVC #Controller programming model -->
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<context:component-scan base-package="controllers"/>
</beans:beans>
Here is my HomeController class
#Controller
public class HomeController {
private UserService userService;
#RequestMapping(value = "/home")
public ModelAndView homePage(HttpServletRequest request, HttpServletResponse response, HttpSession session){
User user = userService.fetchUser();
return new ModelAndView("home", "displayName", user.getUserName());
}
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
}
Here is my User class
public class User {
private String userName;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Here is my UserService class
public class UserService {
public User fetchUser(){
User user = new User();
user.setUserName("test");
return user;
}
}
You miss the #Autowired annotation in your HomeController:
#Autowired
private UserService userService;
Remove homeController bean defintiion.
Since you are using <context:component-scan base-package="controllers"/> there is no need to declare homeController bean - it will be created automatically because it's annotated with annotation #Controller.
Autowire UserService into HomeController using#Autowired` annotation. There are 3 ways you can do it:
field injection:
#Autowired
private UserService userService;
setter injection
#Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
constructor injection:
#Autowired
public HomeController(UserService userService) {
this.userService = userService;
}
Preferred way is to always use constructor injection
Remove UserService getters and setters in HomeController.
In the end, HomeController class should look like:
#Controller
public class HomeController {
private final UserService userService;
#Autowired
public HomeController(UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "/home")
public ModelAndView homePage(HttpServletRequest request, HttpServletResponse response, HttpSession session){
User user = userService.fetchUser();
return new ModelAndView("home", "displayName", user.getUserName());
}
}
This way Eclipse marks the default constructor line with error. If I remove the default constructor glassfish says it cannot initialize UserService because there is no default constructor.
public class UserService {
public UserService(){}
private final UserDao userDao;
#Autowired
public UserService(UserDao userDao){
this.userDao = userDao;
}
public User fetchUser(){
User user = userDao.fetchUser();
return user;
}
}
Related
I'm refactoring an application from Play Framework 2.2.x to Play Framework 2.6.x, and in the updated version of the app an #Autowired spring service is null (I didn't use "new" so that Spring would be confused).
AuthentecatedAction class:
#Scope("prototype")
#Component
public class AuthenticatedAction extends Action<Authenticated>
{
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
ABCUserService ABCUserService;
#Autowired
AccessTokenService accessTokenService;
#Autowired
AccessTokenValidator accessTokenValidator;
#Override
public CompletionStage<Result> call(Http.Context ctx)
{
AccessToken accessToken;
try {
accessToken = accessTokenService.getAccessTokenFromHeader(ctx._requestHeader().headers());
} catch (Throwable throwable) {
return ResultUtils.handleError(new ValidationException(FORBIDDEN, String.format("Access token service is null. %s", throwable.getMessage())));
}
// Some more code here
}
// Some more code here
}
Here accessTokenService is #Autowired, but it is always null.
AccessTokenService class:
#Service
public class AccessTokenService extends JedisBaseService
{
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
private ConfigurationService configurationService;
#Autowired
private EncryptionService encryptionService;
#Inject
public AccessTokenService(JedisPool jedisPool) {
super(jedisPool);
}
// some methods
}
JedisBaseService class:
public class JedisBaseService extends Controller
{
private final Logger log = LoggerFactory.getLogger(getClass());
private JedisPool jedisPool;
#Inject
public JedisBaseService(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
// some methods
// TODO: Check that solution with iserting JedisPool works
// See https://github.com/playframework/play-plugins/tree/master/redis for reference
protected Jedis getJedis()
{
return jedisPool.getResource();
}
}
Please, help me understand why #Autowired in AuthentecatedAction class accessTokenService is always null.
EDIT:
I should probably add, that my build.sbt file contains the line:
// https://mvnrepository.com/artifact/org.springframework/spring-context
"org.springframework" % "spring-context" % "4.3.10.RELEASE"
My routes file contains following lines:
POST /accesstokens #api.controller.accesstoken.AccessTokenController.createAccessToken()
DELETE /accesstokens #api.controller.accesstoken.AccessTokenController.deleteAccessToken()
So, I use dynamic controller dispatching to manage controller instances (by prefixing a controller class name with the # symbol in the routes file).
There is a controller for access tokens:
#Controller
public class AccessTokenController extends ABCController
{
#Autowired
private AccessTokenService accessTokenService;
#Autowired
private ABCUserService ABCUserService;
#Autowired
private ActivityCreationService activityCreationService;
#Autowired
private LiveUpdateService liveUpdateService;
#BodyParser.Of(BodyParser.TolerantJson.class)
#ResponseContentType(type = "accessToken")
public Result createAccessToken() throws ABCControllerException
{
// Implementation
}
public Result deleteAccessToken() throws ABCControllerException
{
// Implementation
}
}
Global object of my application that delegates controller instances management:
public class ABCGlobal
{
private final Logger log = LoggerFactory.getLogger(getClass());
protected static ApplicationContext ctx;
private final ActorSystem system;
#Inject
public ABCGlobal(ActorSystem system) {
this.system = system;
}
public static ApplicationContext getApplicationContext()
{
return ctx;
}
public void onStart(Application app)
{
String springConfigurationName = app.configuration().getString("spring.context");
ctx = new ClassPathXmlApplicationContext(springConfigurationName);
log.info("Loading spring configuration: {}", springConfigurationName);
// Some other code
}
// see: http://typesafe.com/blog/announcing-play-framework-21-the-high-velocit
public <A> A getControllerInstance(Class<A> clazz)
{
return ctx.getBean(clazz);
}
// Some other code
}
Here is the associated conf/components.xml file that is used to configure Spring:
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="api, service"/>
<context:property-placeholder location="classpath:application-base.conf" />
<context:property-placeholder location="classpath:application.conf" />
<bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="resourceConverterFactory">
<property name="serviceLocatorInterface" value="service.resource.conversion.ResourceConverterFactory">
</property>
</bean>
<bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="scraperFactory">
<property name="serviceLocatorInterface" value="service.scraping.ScraperFactory">
</property>
</bean>
<bean id="storageService" class="service.storage.impl.MongoGridFSStorageService"/>
<bean id="configurationService" class="service.configuration.impl.PlayConfigurationService">
<constructor-arg value="com.typesafe.config.Config" name="config">
</constructor-arg>
</bean>
<bean id="mailService" class="service.mail.impl.MailGunApiMailService"/>
</beans>
And in my application-base.conf file I have the following:
# Spring configuration
# ~~~~~
# Define what spring context should be used.
spring.context="components.xml"
I forked an example-spring-data-neo4j project, and build my own 'StigMod' project from it.
Everything works fine, until I tried to change the controller-service-repository structure of this project.
My Controller -> Service -> Repository structure.
#Controller
public class StigModController {
...
#Autowired
StigmodUserDetailsService stigmodUserDetailsService;
...
}
#Service
public class StigmodUserDetailsService implements UserDetailsService {
...
#Autowired
private UserRepository userRepository;
...
}
public interface UserRepository extends GraphRepository<User> {
User findByMail(String mail);
}
Structure in the example:
#Controller
public class AuthController {
...
#Autowired
UserRepository userRepository;
...
}
public interface UserRepository extends GraphRepository<User>, CineastsUserDetailsService {
User findByLogin(String login);
}
#Service
public class CineastsUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
}
public class UserRepositoryImpl implements CineastsUserDetailsService {
#Autowired
private UserRepository userRepository;
}
After my refactoring, a problem popped out:
The StigmodUserDetailsService class is annotated with #Service. Component scan is enabled in package net.stigmod, and my IDE actually could recognize all the #Autowire dependencies in all files.
However, the autowired field stigmodUserDetailsService in StigModController.java could not be initialized during deployment.
I tried several methods proposed by people in some similar questions, but they did not work. I guess there is soming wrong with the spring-security dymamic object feature, but I am not sure.
I posted most of the related complete codes below. Hope someone could help me out. Thank you so much!
### ERROR in Tomcat Localhost Log during deployment ###
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stigModController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: net.stigmod.service.StigmodUserDetailsService net.stigmod.controller.StigModController.stigmodUserDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.stigmod.service.StigmodUserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
...
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: net.stigmod.service.StigmodUserDetailsService net.stigmod.controller.StigModController.stigmodUserDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.stigmod.service.StigmodUserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 56 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [net.stigmod.service.StigmodUserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1308)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 58 more
### web.xml ###
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Spring MVC Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>StigMod</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>StigMod</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
### applicationContext.xml ###
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<context:spring-configured/>
<context:component-scan base-package="net.stigmod">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<tx:annotation-driven mode="proxy"/>
</beans>
### applicationContext-security.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"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<context:component-scan base-package="net.stigmod">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<security:global-method-security secured-annotations="enabled">
</security:global-method-security>
<security:http> <!-- use-expressions="true" -->
<security:intercept-url pattern="/" access="isAnonymous()"/>
<security:intercept-url pattern="/static/**" access="permitAll"/>
<security:intercept-url pattern="/about" access="permitAll"/>
<security:intercept-url pattern="/signin*" access="isAnonymous()"/>
<security:intercept-url pattern="/signup*" access="isAnonymous()"/>
<security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<security:form-login login-page="/signin"
authentication-failure-url="/signin?login_error=true"
default-target-url="/user"
login-processing-url="/signin"
username-parameter="mail"
password-parameter="password"/>
<security:access-denied-handler error-page="/denied" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="stigmodUserDetailsService">
<security:password-encoder hash="md5">
<security:salt-source system-wide="xxxxxx"/>
</security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
</beans>
### StigMod-servelet.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="net.stigmod.controller"/>
<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>
<mvc:annotation-driven/>
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
<property name="configLocation" value="/WEB-INF/velocity.properties"/>
<property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".vm"/>
<property name="contentType" value="text/html;charset=UTF-8"/>
</bean>
</beans>
### Application.java ###
package net.stigmod;
import net.stigmod.util.config.Config;
import net.stigmod.util.config.ConfigLoader;
import net.stigmod.util.config.Neo4j;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.server.Neo4jServer;
import org.springframework.data.neo4j.server.RemoteServer;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableNeo4jRepositories("net.stigmod.repository")
#EnableTransactionManagement
#ComponentScan("net.stigmod")
public class Application extends Neo4jConfiguration {
// Common settings
private Config config = ConfigLoader.load();
private Neo4j neo4j = config.getNeo4j();
private String host = neo4j.getHost();
private String port = neo4j.getPort();
private String username = neo4j.getUsername();
private String password = neo4j.getPassword();
#Override
public SessionFactory getSessionFactory() {
return new SessionFactory("net.stigmod.domain");
}
#Override
#Bean
public Neo4jServer neo4jServer() {
return new RemoteServer("http://" + host + ":" + port, username, password);
}
#Override
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
}
### StigModController.java ###
package net.stigmod.controller;
import net.stigmod.domain.node.User;
import net.stigmod.service.StigmodUserDetailsService;
import net.stigmod.util.config.Config;
import net.stigmod.util.config.ConfigLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#Controller
public class StigModController {
// Common settings
private Config config = ConfigLoader.load();
private String host = config.getHost();
private String port = config.getPort();
#Autowired
StigmodUserDetailsService stigmodUserDetailsService;
private final static Logger logger = LoggerFactory.getLogger(UserController.class);
// front page
#RequestMapping(value="/", method = RequestMethod.GET)
public String index(ModelMap model) {
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "index");
return "index";
}
// about this web app
#RequestMapping(value="/about", method = RequestMethod.GET)
public String about(ModelMap model) {
final User user = stigmodUserDetailsService.getUserFromSession();
model.addAttribute("user", user);
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "about");
return "about";
}
// sign up page GET
#RequestMapping(value="/signup", method = RequestMethod.GET)
public String reg(ModelMap model, HttpServletRequest request) {
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "Sign Up");
// CSRF token
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrfToken != null) {
model.addAttribute("_csrf", csrfToken);
}
return "signup";
}
// sign up page POST
#RequestMapping(value="/signup", method = RequestMethod.POST)
public String regPost(
#RequestParam(value = "mail") String mail,
#RequestParam(value = "password") String password,
#RequestParam(value = "password-repeat") String passwordRepeat,
ModelMap model, HttpServletRequest request) {
try {
stigmodUserDetailsService.register(mail, password, passwordRepeat);
return "redirect:/user";
} catch(Exception e) {
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "Sign Up");
// CSRF token
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrfToken != null) {
model.addAttribute("_csrf", csrfToken);
}
model.addAttribute("mail", mail);
model.addAttribute("error", e.getMessage());
return "signup";
}
}
// sign in page GET (POST route is taken care of by Spring-Security)
#RequestMapping(value="/signin", method = RequestMethod.GET)
public String login(ModelMap model, HttpServletRequest request) {
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "Sign In");
// CSRF token
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
if (csrfToken != null) {
model.addAttribute("_csrf", csrfToken);
}
return "signin";
}
// sign out
#RequestMapping(value="/signout", method = RequestMethod.GET)
public String logout (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/signin";
}
// Denied page GET
#RequestMapping(value="/denied", method = RequestMethod.GET)
public String reg(ModelMap model) {
model.addAttribute("host", host);
model.addAttribute("port", port);
model.addAttribute("title", "Denied");
return "denied";
}
}
### UserRepository.java ###
package net.stigmod.repository.node;
import net.stigmod.domain.node.User;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends GraphRepository<User> {
User findByMail(String mail);
}
### StigmodUserDetails.java ###
package net.stigmod.service;
import net.stigmod.domain.node.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
public class StigmodUserDetails implements UserDetails {
private final User user;
public StigmodUserDetails(User user) {
this.user = user;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
User.SecurityRole[] roles = user.getRole();
if (roles == null) {
return Collections.emptyList();
}
return Arrays.<GrantedAuthority>asList(roles);
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getMail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
}
### StigmodUserDetailsService.java ###
package net.stigmod.service;
import net.stigmod.domain.node.User;
import net.stigmod.repository.node.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
public class StigmodUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
public StigmodUserDetailsService() {}
public StigmodUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public StigmodUserDetails loadUserByUsername(String mail) throws UsernameNotFoundException {
final User user = userRepository.findByMail(mail);
if (user == null) {
throw new UsernameNotFoundException("Username not found: " + mail);
}
return new StigmodUserDetails(user);
}
public User getUserFromSession() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
Object principal = authentication.getPrincipal();
if (principal instanceof StigmodUserDetails) {
StigmodUserDetails userDetails = (StigmodUserDetails) principal;
return userDetails.getUser();
}
return null;
}
#Transactional
public User register(String mail, String password, String passwordRepeat) {
User found = userRepository.findByMail(mail);
if (found != null) {
throw new RuntimeException("Email already taken: " + mail);
}
if (password == null || password.isEmpty()) {
throw new RuntimeException("No password provided.");
}
if (passwordRepeat == null || passwordRepeat.isEmpty()) {
throw new RuntimeException("No password-repeat provided.");
}
if (!password.equals(passwordRepeat)) {
throw new RuntimeException("Passwords provided do not equal.");
}
User user = userRepository.save(new User(mail, password, User.SecurityRole.ROLE_USER));
setUserInSession(user);
return user;
}
void setUserInSession(User user) {
SecurityContext context = SecurityContextHolder.getContext();
StigmodUserDetails userDetails = new StigmodUserDetails(user);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(), userDetails.getAuthorities());
context.setAuthentication(authentication);
}
}
i am relatively new in spring and i have problems with understanding fundamentals of spring mvc..
My controller
#Controller
public class HomeController {
private ContactManager contactManager;
#Autowired
public void setContactManager(ContactManager contactManager) {
this.contactManager = contactManager;
}
#RequestMapping(value="/")
public ModelAndView listContact(ModelAndView model) throws IOException {
List<Contact> listContact = contactManager.list();
model.addObject("listContact", listContact);
model.setViewName("home");
return model;
}
...
Contact ManagerImpl, and implementation of interface method
public class ContactManagerImpl implements ContactManager {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
...
ContactManager
public interface ContactManager {
public void saveOrUpdate(Contact contact);
public void delete(int contactId);
public Contact get(int contactId);
public List<Contact> list();
}
And root-context.xml
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="sec"/>
</bean>
<bean id="managmentService" class="spring.contact.impl.model.ContactManagerImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
My problem is that i get error:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of
type [spring.contact.api.ContactManager] found for dependency: expected at least 1
bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
And of course the problem is with #Autowired annotations. How can i fix it ? When i removed #Autowired annotations i get another error: NullPointerException (manager in HomeController).
Your Controller should be..
#Controller
public class HomeController {
#Autowired
#Qualifier("managmentService")
private ContactManager contactManager;
#RequestMapping(value="/")
public ModelAndView listContact(ModelAndView model) throws IOException {
List<Contact> listContact = contactManager.list();
model.addObject("listContact", listContact);
model.setViewName("home");
return model;
}
...
In your configuration add also <context:annotation-config />that is used in order to be able to use #Autowired annotation.
An example of <context:annotation-config />
<?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">
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>
Move the #Autowired annotation to the declaration for ContactManager.
You won't need the setter, delete it.
I'm quite new to spring and i'having this issue with spring security.
Actually it only works without my custom UserDetailsService implementation.
Account and Role Objects
#Entity
#Table(name="ACCOUNT", uniqueConstraints = {#UniqueConstraint (columnNames = "USERNAME"), #UniqueConstraint (columnNames = "EMAIL")})
public class Account implements Serializable {
private static final long serialVersionUID = 2872791921224905344L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="ID")
private Integer id;
#Column(name="USERNAME")
#NotNull
private String username;
#Column(name="PASSWORD")
#NotNull
private String password;
#Column(name="EMAIL")
#NotNull
#Email
private String email;
#Column(name="ENABLED")
private boolean enabled;
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="ACCOUNT_ROLE", joinColumns = {#JoinColumn (name="ID_ACCOUNT")}, inverseJoinColumns ={ #JoinColumn (name="ID_ROLE")})
private Set<Role> roles = new HashSet<Role>(0);
Role
#Entity
#Table(name="ROLE", uniqueConstraints={#UniqueConstraint (columnNames="NAME")})
public class Role implements Serializable{
private static final long serialVersionUID = -9162292216387180496L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "NAME")
#NotNull
private String name;
#ManyToMany(mappedBy = "roles")
private Set<Account> accounts = new HashSet<Account>(0);
The adapter for the UserDetails
#SuppressWarnings({ "serial", "deprecation" })
public class UserDetailsAdapter implements UserDetails {
private Account account;
public UserDetailsAdapter(Account account) {this.account = account;}
public Account getAccount() {return account;}
public Integer getId(){return account.getId();}
public String getEmail () {return account.getEmail();}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for (Role r :account.getRoles()) {
authorities.add(new GrantedAuthorityImpl(r.getName()));
}
return authorities;
}
The custom UserDetailsService
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
#Inject AccountDao accountDao;
#Inject RoleDao roleDao;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Account account= accountDao.findByUsername(username);
if(account==null) {throw new UsernameNotFoundException("No such user: " + username);
} else if (account.getRoles().isEmpty()) {
throw new UsernameNotFoundException("User " + username + " has no authorities");
}
UserDetailsAdapter user = new UserDetailsAdapter(account);
return user;
}
The web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/appServlet/security- context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Creates the Filters to handle hibernate lazyload exception -->
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
the root-context
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
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-3.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:property-placeholder properties-ref="deployProperties"/>
<!-- Remember to correctly locate the right file for properties configuration(example DB connection parameters) -->
<bean id="deployProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/spring/appServlet/spring.properties" />
<context:annotation-config/>
<context:component-scan base-package="org.treci">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<import resource="/appServlet/data-context.xml"/>
The security context
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<http pattern="/resources" security="none" />
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/" access="permitAll"/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/login-success" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/login"
default-target-url="/login-success"
authentication-failure-url="/login-failed"/>
<logout logout-success-url="/logout"/>
</http>
<beans:bean id="customUserDetailsService" class="org.treci.app.service.CustomUserDetailsService"></beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
</authentication-provider>
</authentication-manager>
</beans:beans>
I hope some of you can help, save me :)
Here's how I solved the problem:
in the CustomDetailsService I was returning a UserDetails Object by using an Adapter as you can see.
Looking at some tutorial I realized that i should return an org.springframework.security.core.userdetails.User Object.
Here's my new CustomDetailsService implementation:
#Transactional(readOnly=true)
#Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
#Inject AccountDao accountDao;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Account account= accountDao.findByUsername(username);
if(account==null) {throw new UsernameNotFoundException("No such user: " + username);
} else if (account.getRoles().isEmpty()) {
throw new UsernameNotFoundException("User " + username + " has no authorities");
}
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
account.getUsername(),
account.getPassword().toLowerCase(),
account.isEnabled(),
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(account.getRoles()));
}
public List<String> getRolesAsList(Set<Role> roles) {
List <String> rolesAsList = new ArrayList<String>();
for(Role role : roles){
rolesAsList.add(role.getName());
}
return rolesAsList;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
public Collection<? extends GrantedAuthority> getAuthorities(Set<Role> roles) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRolesAsList(roles));
return authList;
}
}
I'm using Spring 3.1.0.RELEASE. I'm having trouble autowiring a private variable from a Spring4JUnitRunner class. My JUnit class is ...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "file:src/main/webapp/WEB-INF/dispatcher-servlet.xml" })
public class RegistrationControllerTest {
#Autowired
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
private RegistrationController controller;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
// I could get the controller from the context here
controller = new RegistrationController();
final RegistrationValidation registrationValidation = new RegistrationValidation();
controller.setRegistrationValidation(registrationValidation);
}
Not sure if its relevant, but here's my dispathcer-servlet.xml file ...
<?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:p="http://www.springframework.org/schema/p"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- Enable annotation driven controllers, validation etc... -->
<mvc:annotation-driven />
<context:component-scan base-package="com.myco.eventmaven" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/messages" />
</bean>
</beans>
Here is my controller. The "usersDao" field is null during my test, causing NullPointerExceptions (works fine when I run it as a normal webapp in JBoss) ...
#Controller
#RequestMapping("/registrationform.jsp")
public class RegistrationController {
private static Logger LOG = Logger.getLogger(RegistrationController.class);
#Autowired
private RegistrationValidation registrationValidation;
#Autowired
private UsersDao usersDao;
public void setRegistrationValidation(
RegistrationValidation registrationValidation) {
this.registrationValidation = registrationValidation;
}
// Display the form on the get request
#RequestMapping(method = RequestMethod.GET)
public String showRegistration(Map model) {
LOG.debug("called GET method.");
final Registration registration = new Registration();
model.put("registration", registration);
return "user/registrationform";
}
// Process the form.
#RequestMapping(method = RequestMethod.POST)
public String processRegistration(Registration registration, BindingResult result) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String nextPage = "user/registrationform";
// set custom Validation by user
registrationValidation.validate(registration, result);
if (result.hasErrors()) {
return nextPage;
} else {
// Save the user to the database.
if (usersDao.saveUser(registration)) {
nextPage = "user/registrationsuccess";
} // if
} // if
return nextPage;
}
The class of the member field, usersDao, is annotated with the #Component annotation ...
#Component("usersDao")
public class UsersDaoImpl implements UsersDao {
What additional configuration do I need to add to properly autowire the dao object in my JUnit class? Thanks, -
You're getting the null because you're instantiating RegistrationController yourself, instead of getting the bean from Spring. You almost figured that out for yourself:
// I could get the controller from the context here
controller = new RegistrationController();
You could, and you should. Remove those two lines, and use the following on the field declartion:
#Autowired
private RegistrationController controller;