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