Autowiring bean in Session Listener - spring

I need to Autowire a service class annotated with #Service annotation in my session listener class as I need to perform some DB operation on session destroyed method. I am not able to autowire the service class as I have added the listener in my web.xml and it is no longer spring managed. I have tried several options(workarounds) like getting a bean from application context via servlet context but I am not getting any beans in that way.
Following are my classes:-
MyService:
#Service
#Transactional
public class FxTransactionService{
//some autowirings
public void performDBoperation(Long id)
{
//business logic
}
}
Session Listener:
public class SessionHandler implements HttpSessionListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private MyService myService;
#Override
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("Session created");
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(arg0.getSession()
.getServletContext());
System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
//This gives me empty list
}
#Override
public void sessionDestroyed(HttpSessionEvent arg0) {
Long id = (Long) arg0.getSession().getAttribute("Id");
myService.performDBoperation(id);
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.abc.controller.SessionHandler</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<filter>
<filter-name>preAuthHeaderAdditionFilter</filter-name>
<filter-class>com.abc.filter.PreAuthHeaderAdditionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>preAuthHeaderAdditionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- <filter> <filter-name>openEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter> <filter-mapping> <filter-name>openEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern> </filter-mapping> -->
</web-app>

First install the Spring listener ContextLoaderListener.
In your own listener you can access the context using WebApplicationContextUtils.
It is not autowiring though, you have to fetch the required bean/service yourself.

Related

Spring MVC (5.0.8) with Spring Security (5.0.7) Basic Authentication not working

I'm trying to enable Spring Security in my Spring MVC application which serves some REST web services (Java 8). The problem I have is whatever I do the auth just doesn't work at all. I can access my REST endpoints without any credentials. I use this manual: https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/htmlsingle/
Git repo with full code of my app is here: https://github.com/SP8EBC/MKS_JG_ONLINE
SecurityConfig.java looks as follows
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(Secret.user).password("{noop}" + Secret.password).roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// http
// .csrf()
// .disable()
// .authorizeRequests().antMatchers("/**").permitAll()
// .anyRequest().authenticated()
// .and()
// .httpBasic()
// .realmName("test")
// .authenticationEntryPoint(new CustomAuthenticationEntryPoint());
http.authorizeRequests().anyRequest().denyAll();
}
}
AppConfig.java
#Configuration
#Import(SecurityConfig.class)
#EnableWebMvc
#EnableSpringDataWebSupport
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"pl.jeleniagora.mks.dao.repository"})
#ComponentScan("pl.jeleniagora.mks")
public class AppConfig{
// beans and app config
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>MKS_JG_ONLINE</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>pl.jeleniagora.mks.ws.config</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>pl.jeleniagora.mks.ws.controllers</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file />
</welcome-file-list>
</web-app>
When I start the Tomcat 8.5 in debug mode I see that the SecurityConfig loads (execution stops at breakpoint in configure and configureGlobal). What I'm doing wrong?
Spring Security requires, next to the security configuration, a servlet filter to be registered.
Add the following to your web.xml (explained here).
<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>
This will add the filter and will be applied to all requests.
However as you are using a recent servlet container I would suggest to ditch the web.xml and create 2 java classes to do the bootstrapping. (See also here).
First bootstrap your application
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public Class<?>[] getServletConfigClasses() {
return new Class[] { WebConfig.class }; // or whatever it is called or return `null`
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
}
Then add the one that bootstraps/configures Spring Security filter
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
Now everything is configured in Java and you can do without your web.xml.

Spring context loader

I have a question about spring context. My application's using spring and spring scheduler.
In web.xml, i declared:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
My question is:
If I declared org.springframework.web.context.ContextLoaderListener in web.xml, the scheduler will run twice, all beans are duplicate, and App start-up time about 160 seconds.
If I remove org.springframework.web.context.ContextLoaderListener,
spring throws exception: No WebApplicationContext found: no ContextLoaderListener registered. And App start-up time reduce to 80 seconds.
How can i solve it? Thanks all!
Thanks #M.Deinum, but i don't understand your idea.
Here is my web.xml:
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.htc.epos.api.bootstrap</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>webapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.htc.epos.api.bootstrap.WebAppConfig
com.htc.epos.api.bootstrap.AppConfig
</param-value>
</init-param>
</servlet>
Think #M.Deinum is right; split your beans via what is remoting and what is normal. I do this in my web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/root-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>remoting</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/spring/remoting-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>remoting</servlet-name>
<url-pattern>/remoting/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
root-context.xml contains all my normal beans (services, helpers, calculator, jms listeners, scheduled tasks, etc).
remoting-servlet.xml only specifies those services that need to be exposed via the HttpInvokerServiceExporter. There are no imports or links to beans defined in the root, other than things like ref="historyWebService" for the exporter.
From what I understand, you end up with 2 application context: 1 root and 1 remoting. The remoting one inherits all the beans from the root so you don't declare or instantiate beans twice (i think)!!! I certain don't appear to have duplicate beans produced (i.e. 2 task, 2 jms listeners, etc).
I have 2 file config:
1. AppConfig:
#Configuration
#EnableScheduling
#EnableTransactionManagement
#EnableJpaRepositories("com.test.api.repository")
#PropertySource("classpath:application.properties")
public class AppConfig {
...............
}
2. WebInitializer
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.test.api"})
public static class WebAppConfig extends WebMvcConfigurerAdapter {
...................
}
}
In WebAppConfig, if I change #ComponentScan(basePackages = {"com.test.api"}) to web package #ComponentScan(basePackages = {"com.test.api.web"}), so spring bean's not duplicate and scheduler not run twice. But sometime it throw exception:
error: org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Spring Mobile inside AuthenticationFailureHandler not found

I am development success and failure handlers in Spring Security.
Depends device type I must show one html view or send one json response. To this purpose I use Spring Mobile, but when I create Device object with HtttpServletRequest not found. Some idea?
web.xml
<filter>
<filter-name>deviceResolverRequestFilter</filter-name>
<filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>
ApplicationContext.xml
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.mobile.device.DeviceWebArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
Class
public class AuthFailureHandler implements AuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
Device device = DeviceUtils.getCurrentDevice(request);
if(device.isNormal()){
response.sendRedirect(response.encodeRedirectURL("./userNoAuth"));
} else {
response.sendRedirect(response.encodeRedirectURL("./rest/userNoAuth"));
}
}
}
Error
java.lang.NullPointerException at com.myapp.security.handler.AuthFailureHandler.onAuthenticationFailure(AuthFailureHandler.java:19)
UPDATE:
I am change .getCurrentDevice(HttpServletRequest) method to getRequiredCurrentDevice(HttpServletRequest).
Now I get this error.
java.lang.IllegalStateException: No currenet device is set in this request and one is required - have you configured a DeviceResolvingHandlerInterceptor?
Verify your web.xml contains a filter-mapping for the deviceResolverRequestFilter. The following is a working example from the Spring Mobile Samples repository. Hope that helps!
https://github.com/SpringSource/spring-mobile-samples/tree/master/lite-device-resolver-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/root-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>
<!-- Use the DeviceResolverRequestFilter OR the DeviceResolverHandlerInterceptor in the servlet-context.xml -->
<filter>
<filter-name>deviceResolverRequestFilter</filter-name>
<filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>deviceResolverRequestFilter</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>

Does the new Spring MVC Test Framework released in Spring 3.2 test the web.xml configuration?

I've read the docs ( http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework ) several times and I can't confirm if the WebApplicationContext context that gets injected when you use the #WebApplicationContext annotation is actually looking at the web.xml.
In other words, I want to test my web.xml configuration. The filters and servlet path specifically. But when I configure my test it ignores the web.xml. (e.g. I try a get request on a URL like this /myServletPath/foo and it fails with a 404.)
My test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration({
"classpath*:WEB-INF/config/application-context.xml",
"classpath*:WEB-INF/oms-servlet.xml",
"classpath*:persistence-context.xml"
})
public class OrderSummaryControllerIntegrationTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void testFindOrderSummariesExpectsSuccess() throws Exception {
mockMvc.perform(get("/oms/orders?user=1234&catalog=bcs"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
And my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
version="2.5">
<display-name>OMS REST Services</display-name>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<filter>
<filter-name>webappMetricsFilter</filter-name>
<filter-class>com.yammer.metrics.web.DefaultWebappMetricsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>webappMetricsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/application-context.xml, classpath*:persistence-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>oms</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>oms</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
You are right, Spring-mvc-test does not read the web.xml file, but you can configure the filters this way:
webAppContextSetup(this.wac).addFilter(new DefaultWebappMetricsFilter(), "/*").build()

NoSuchBeanDefinitionException with spring and gwt (requestFactory)

I get this error with a gwt (using requestfactory) and spring
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.calibra.server.service.AccountService] is defined: expected single bean but found 0:
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:271)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at org.calibra.server.SpringServiceLocator.getInstance(SpringServiceLocator.java:24)
at com.google.web.bindery.requestfactory.server.LocatorServiceLayer.createServiceInstance(LocatorServiceLayer.java:56)
My service locator
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(
RequestFactoryServlet.getThreadLocalServletContext());
return context.getBean(clazz);
}
}
My spring service
#Service
public class AccountServiceImpl implements AccountService{
#Override
public void addNewAccount(Account account) {
...
}
#Override
public List<Account> loadAllAccounts() {
...
}
}
Gwt requestContext, reference my spring service
#Service(value=AccountService.class, locator=SpringServiceLocator.class)
public interface AccountRequest extends RequestContext {
Request<Void> addNewAccount(AccountProxy account);
Request<List<AccountProxy>> loadAllAccounts();
}
my web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>gwtRequest</servlet-name>
<servlet-class>com.google.web.bindery.requestfactory.server.RequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gwtRequest</servlet-name>
<url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>welcomeGWT.html</welcome-file>
</welcome-file-list>
I don't understand how i can have 0 AccountService beans ?
i tried to add in the dispatcher-servlet
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
I got the same result
Any idea?
edit: if somebody have a full complete example, that could be useful.
I think using the ContextLoaderListener alone is not enough as you don't seem to have the DispatcherServlet in use (have you?).
The following lines work for me:
<filter>
<filter-name>springRequestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springRequestContextFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
I've seen this question in a couple of other places. You should try explicity defining the AccountServiceImpl as a bean in your applicationContext.xml (not the dispatch-servlet.xml) first and see if you still get the error, if you don't then you know it's that you're missing the component-scan in your application context xml which is what I think is the case.
hope this helps

Resources