mercredi 4 mars 2015

Spring MVC - importing spring-security to pom.xml causes @Autowired problems

First of all, it's my post here and I would like to humbly say hi to the SO community - I've found a lot of answers for various issues and got a whole bunch of problems resolved here.


But I stumbled upon one issue, that I couldn't find a solution to. I'd like to point out the fact here, that I've been developing for a month, so I'm on a beginner level in Spring.


I am developing a backend RESTful webservice which handles and processes requests from dedicated Android and iOS Apps. I am using Spring MVC template (version 4.1.4.RELEASE) on the server-side and Wildfly 8.2 as a container.


Now, I have to implement a hashing algorithm to encode User activation password and unique Device identifier (currently these values are stored in database in raw plaintext), and match the input to the hashed values.


Spring Security provides a convenient BCrypt Password encoder/matcher class which fully satisfies my needs (I do not need other Spring Security functionalities, like filter chaining etc., but I will use them eventually, when I become fluent with Spring and grasp the Security concept).


The first step of implementing Spring-Security to my project already comes as a showstopper - importing spring-security-config and/or spring-security-core pom.xml dependency and trying to deploy my app, causes proxying errors during context initialization in my service class:



10:02:00,845 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC000001: Failed to start service jboss.undertow.deployment.default-server.default-host./atwork: org.jboss.msc.service.StartException in service jboss.undertow.deployment.default-server.default-host./atwork: Failed to start service
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1904) [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_25]
at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_25]
Caused by: java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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 io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:222)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:87)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentService.start(UndertowDeploymentService.java:72)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1948) [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1881) [jboss-msc-1.2.2.Final.jar:1.2.2.Final]
... 3 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
at io.undertow.servlet.core.ApplicationListeners.contextInitialized(ApplicationListeners.java:173)
at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:193)
... 7 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)
... 22 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)
... 24 more

10:02:00,875 ERROR [org.jboss.as.controller.management-operation] (Controller Boot Thread) JBAS014613: Operation ("deploy") failed - address: ([("deployment" => "atwork.war")]) - failure description: {"JBAS014671: Failed services" => {"jboss.undertow.deployment.default-server.default-host./atwork" => "org.jboss.msc.service.StartException in service jboss.undertow.deployment.default-server.default-host./atwork: Failed to start service
Caused by: java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)}
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'authenticationService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)}
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private pl.atwork.dao.EmployeeDao pl.atwork.service.AuthenticationService.empDao; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [pl.atwork.dao.EmployeeDao] 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)}"}}
10:02:01,022 INFO [org.jboss.as.server] (ServerService Thread Pool -- 28) JBAS018559: Deployed "atwork.war" (runtime-name : "atwork.war")
10:02:01,089 INFO [org.jboss.as.controller] (Controller Boot Thread) JBAS014774: Service status report
JBAS014777: Services which failed to start: service jboss.undertow.deployment.default-server.default-host./atwork: org.jboss.msc.service.StartException in service jboss.undertow.deployment.default-server.default-host./atwork: Failed to start service


Here's the dependency from pom.xml that causes the problem (the version is 3.2.6.RELEASE)



<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security-version}</version>
</dependency>


When I comment out the spring-security dependencies, autowiring executes correctly, and my app deploys successfully.


web.xml



<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://ift.tt/nSRXKP" xmlns:xsi="http://ift.tt/ra1lAU"
xsi:schemaLocation="http://ift.tt/nSRXKP http://ift.tt/1eWqHMP"
version="3.0">

<display-name>ATWork Webservice</display-name>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security-root-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>


I have two context XML files - root-context and servlet-context


root-context.xml



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

<beans xmlns="http://ift.tt/GArMu6"
xmlns:xsi="http://ift.tt/ra1lAU" xmlns:c="http://ift.tt/MffYna"
xmlns:context="http://ift.tt/GArMu7"
xmlns:jdbc="http://ift.tt/18IIlo0" xmlns:tx="http://ift.tt/OGfeU2"
xmlns:aop="http://ift.tt/OpNdV1" xmlns:jee="http://ift.tt/OpNaZ5"
xmlns:security="http://ift.tt/1c8inpe"
xsi:schemaLocation="http://ift.tt/18IIlo0 http://ift.tt/11PpghP
http://ift.tt/GArMu6 http://ift.tt/1CZCNBy
http://ift.tt/GArMu7 http://ift.tt/1tY3uA9
http://ift.tt/OpNdV1 http://ift.tt/1oTOpwm
http://ift.tt/OpNaZ5 http://ift.tt/1sC4L0N
http://ift.tt/OGfeU2 http://ift.tt/UJ63uf
http://ift.tt/1c8inpe http://ift.tt/1epvZ6L">

<!-- Root Context: defines shared resources visible to all other web components -->

<!-- PROPERTY FILES -->
<context:property-placeholder location="classpath:database.properties"
ignore-unresolvable="true" />

<!-- DATABASE BEANS -->

<context:component-scan base-package="pl.atwork" />

<!-- Enable annotation based transaction management (@Transactional) -->
<tx:annotation-driven transaction-manager="transactionManager" />

<!-- Datasource -->

<jee:jndi-lookup id="dataSource" jndi-name="${database.jndi-name}"
expected-type="javax.sql.DataSource" />

<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="pl.atwork.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${database.hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.dialect">${database.hibernate.dialect}</prop>
</props>
</property>
</bean>

<!-- Exception translation bean post processor: translates database specific
HibernateException or SQLExceptions into Spring exceptions that can be understood
by the application context. -->
<bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

<!-- Hibernate Transaction Manager bean: controls the transactions as well
as roll-backs. -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>


servlet-context.xml



<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://ift.tt/1bHqwjR"
xmlns:xsi="http://ift.tt/ra1lAU" xmlns:beans="http://ift.tt/GArMu6"
xmlns:context="http://ift.tt/GArMu7" xmlns:tx="http://ift.tt/OGfeU2"
xmlns:mvc="http://ift.tt/1bHqwjR"
xsi:schemaLocation="http://ift.tt/1bHqwjR http://ift.tt/YzZ6hD
http://ift.tt/GArMu6 http://ift.tt/1CZCNBy
http://ift.tt/GArMu7 http://ift.tt/1tY3uA9
http://ift.tt/OGfeU2 http://ift.tt/UJ63uf">

<!-- DispatcherServlet Context: defines this servlet's request-processing
infrastructure -->

<!-- Enables the Spring MVC @Controller programming model -->
<mvc:annotation-driven />

<context:component-scan base-package="pl.atwork.service" />
<tx:annotation-driven transaction-manager="transactionManager" />

<!-- Handles HTTP GET requests for /resources/** by efficiently serving
up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />

<!-- Resolves views selected for rendering by @Controllers to .jsp resources
in the /WEB-INF/views directory -->
<beans:bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>

<!-- Configure bean to convert JSON to POJO and vice versa -->
<beans:bean id="jsonMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</beans:bean>

<!-- Configure to plugin JSON as request and response in method handler -->
<beans:bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<beans:property name="messageConverters">
<beans:list>
<beans:ref bean="jsonMessageConverter" />
</beans:list>
</beans:property>
</beans:bean>
</beans:beans>


My project structure seems OK, which is proved by valid initialization, when not importing Spring-Security dependencies:



pl.atwork.dao
---EmployeeDao.java
...
pl.atwork.model
---Employee.java
...
pl.atwork.service
---AuthenticationService.java
...
pl.atwork.service.controller
---RestController.java


RestController class, which Autowires services:



@Controller
public class RestController {

@Autowired
private AuthenticationService authService;

... other Autowired services fields and controller methods


AuthenticationService class, where proxying errors start on the first @Autowired annotation:



@Service
public class AuthenticationService implements Serializable {

@Autowired
private EmployeeDao empDao;

... other Autowired fields and @Transactional methods


EmployeeDao class, which could not be resolved when Autowiring:



@Repository
public class EmployeeDao implements Serializable {

@Autowired
private SessionFactory sessionFactory;

... typical DAO methods


I've tried enforcing GGLib proxying in context files, but it doesn't work for me. I also tried to use @Qualifier by type and name annotation for my @Autowired fields and it did not work as well.


It seems like importing spring-security dependencies entirely mess up my @Autowired configuration, and I'm out of ideas at this point. My latest clue is that maybe my component scanning in context files is a little awkward, but I do not know how to make it look and work better. Also, I am not quite sure whether I should be using interfaces in my project, and Autowire them instead of concrete implementations. I don't find a good reason to use interfaces in my project, as its not-too-wide scope is quite not worth it.


Any help would be much appreciated - I'm stuck with this for a few days now.


Aucun commentaire:

Enregistrer un commentaire