Why is my Spring ContextRefreshed event called twice? - java

I have a Spring ApplicationListener bean registered to listen for ContextRefreshed events. For some odd reason though, I get two calls to the onApplicationEvent(ContextRefreshedEvent) method at the completion of the context initialization. Is this normal behavior or is it indicative of a problem with my configuration? I'm using Jetty 8 for my Servlet container.
My relevant web.xml configuration is as follows
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/spring/spring-config.xml</param-value>
</context-param>
<servlet>
<servlet-name>Spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet-mapping>
<servlet-name>Spring</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
Thanks!

Even though you did not specify a contextConfigLocation for your DispatcherServlet it still creates a child context and the second refreshed event is for that context. Use event.getApplicationContext() to find out which context the event is for.

it happened to me as well, on a different event-listener. (ApplicationListener<AuthenticationFailureBadCredentialsEvent>)
I suspected the ContextLoaderListener, and when I removed the declaration from the web.xml, the app was working properly. Then I had to figure out what is its purpose, of the ContextLoaderListener...
Role/Purpose of ContextLoaderListener in Spring?
the interesting answer there is:
ContextLoaderListener is optional. Just to make a point here: you can
boot up a Spring application without ever configuring
ContextLoaderListener ...just the basic minimum web.xml with
DispatcherServlet

It looks like bug.
https://jira.springsource.org/browse/SPR-6589
If you are using 3.0 try it on the latest available release which is 3.05.

I had this problem too but fixed it. I was injecting the dataSource into my DAO (and instantiating a JdbcTemplate with it)....but I also had a Spring bean configured for JDBCTemplate.
I should have been injecting my DAO with the jdbcTemplate...that avoids the duplicate.

Related

Spring - how to load properties prior to context loader getting called

I would like to read the properties from my properties files in my custom contextLoadListener. However my listener class gets executed prior to spring loading the properties file. How do I get my 'rdbaccess.properties' loaded prior to the execution of my CustomContextLoaderListener class? Below is my relevant parts of the configuration.
In web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
com.my.package.setup.CustomContextLoaderListener
</listener-class>
</listener>
In spring-servlet.xml
<context:component-scan base-package="com.my.package" />
<context:property-placeholder location="classpath:rdbaccess.properties" />
The ContextLoader(Listener) is the instance that initializes your Spring context. This has to be the very first thing happening in the lifecycle of your (Spring) application, at all. It's not possible that your properties get loaded before the context has initialized. You can't decorate your living room before you've built the house :)
However, regarding the name of your properties, you probably need to inject the database credentials into your beans. This issue can be resolved with another approach, which is a lot cleaner in my opinion, especially for applications that run in multiple environments.
Instead of keeping/putting the credentials in your classpath, you should configure properties that change for each environment (local, development, integration, production ...) right there. For example, if you run a Tomcat, then put a properties file into the conf directory containing your database credentials. You can access that file in your custom ContextLoaderListener and provide the properties to your beans through a class with a static properties map, for instance.
Try adding a custom context initializer to load your properties file, like so:
https://gist.github.com/rponte/3989915

Spring cloud Zuul with servlet-api 2.5

#EnableZuulProxy doesn't work under a servlet 2.5 container. Is there any workaround to get spring-cloud zuul work under a servet 2.5 container?
Also I could not find the annotation processor of #EnableZuulProxy. Please provide the class which propesses #EnableZuulProxy so that I can better understand what this annotation really does.
Spring Cloud is meant to be run on servlet 3.0. That being said, it is possible to get #EnableZuulProxy running on servlet 2.5. I had to figure out a hack for this as I had to get this working in Tomcat 6.
The main issue is due to the ZuulConfiguration.class, which has the method:
#Bean
#ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() { ... }
The issue here is that ServletRegistrationBean uses javax.servlet.Registration$Dynamic, which is not available until Servlet 3.0. This results in a NoClassDefFoundError.
To work around this, use the spring-boot-legacy project to first register a DispatcherServlet. Secondly, you'll have to manually create a zuul servlet.
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">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.dm.gateway.microservicegateway.Application</param-value>
</context-param>
<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>ContextLifecycleFilter</filter-name>
<filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ContextLifecycleFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>zuul</servlet-name>
<servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
I found the best way to remove the auto servlet registration, is to just make a verbatim copy of ZuulConfig called ZuulOverrideConfig, and remove the zuulServlet() method. This is because the ZuulProxyConfiguration extends ZuulConfiguration, and it seemed to create the bean even when i tried to override it. I'm not 100% on the mechanics behind this, so there may be a better way.
The second change I made in ZuulOverrideConfig was to call an extended implementation of ZuulFilterInitializer, called 'LegacyZuulFilterInitializer`. This is because for some reason, the Zuul servlet was being crated, and able to be invoked, but no filters were bootstrapped. This extension is a hacky way to get the filters to bootstrap.
Next, I created a copy of ZuulProxyConfiguration called ZuulLegacyProxyConfiguraiton, and had it extend ZuulOverrideConfig.class.
Finally, I annotated the Application class as follows.
#EnableCircuitBreaker
#EnableDiscoveryClient
#Import(ZuulLegacyProxyConfiguration.class)
#SpringBootApplication
public class Application {....}
After all these hacks, the implementation finally worked as expected. I wouldn't suggest using this for long, as it's pretty hacky. You won't get configuration class updates automatically when moving to new versions, and I can't guarantee that something won't break randomly!
This is using Spring Cloud 1.1.4.RELEASE
Gist of all the code.
#EnableZuulProxy is from Spring Cloud which is based on Spring Boot which is Servlet 3.0 and above. If you need to use Servlet 2.5 you can use the Netflix APIs directly.
#EnableZuulProxy is meta-annotated with #Import(ZuulProxyConfiguration.class) so I guess that's what you mean when you say "propesses"? If you don't know what an #Import is, go and read up on Spring.

Spring using different contexts and SpringBeanAutowiringSupport in jsp

I have the following problem. My multi module spring application is loading up two different contexts at startup.
Context one is loaded up with this code snippet.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
and the other one via the rest webservice
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/components_webservice.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Now I got a jsp using this command to use autowired beans inside the jsp.
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
This only makes it possible to autowire the beans defined in the main context. I can't autowire beans from the REST Servlet declared context. Is there a way to make this context available inside the jsp? I know one way would be to declare all the beans inside the main context, but I want them to keep separated.
Greetings,
smoothny
Dont put any logic inside your jsp. Autowire fields in your controllers and pass generated values to the model.

Spring MVC: Using #Autowire is getting references to different spring bean instances

I have a UserCredetnialsDatSourceAdapter defined in my Spring app context file. I also have a custom filter added to the Spring via the DelegatingFilterProxy.
This filter is using the #Autowire to get a reference to the DataSource Bean. I also #Autowire the DataSource in my DAO. When I debug I see different instance id's to the datasource in the Filter and DAO instances. Why are there 2 instances whenthese are singletons by default?
I also fired up jvisualvm and I looked at the heap and all my beans in my app context have 2 instances? Thanks for any insight maybe the bean pre/post processing has something do with it or maybe I should not be using #Autowire in a Filter. Any help is apprciated. Thanks!
EDIT
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config-context.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/web-context.xml,/WEB-INF/config-context.xml</param-value>
</init-param>
Interesting I think I see what it going on. There are 2 instances because there are 2 contexts. One for the app and 1 for each request (Thread) I assume? Is there a way to specfiy which context to use? Maybe filter is not the answer and I will need to use AOP. Need to research how to inject a bean in #Aspect, if that is even possible.
Thanks!!
-Joe
You are importing your /WEB-INF/config-context.xml as part of your Root Application Context(one loaded up by ContextLoaderListener) as well as your Web Context(loaded by DispatcherServlet). You can probably remove it from the one for DispatcherServlet.

Two separate Spring contexts for one webapp

I want to use two different Spring web contexts, each have own contextConfig, spring servlet and filter, that should be mapped to different urls. I have a
Standard Grails project, mapped to '/'
And an existing Spring webapp, that I want to map to /extra/
I know that I can deploy both into one Tomcat, but I'm looking for a way of making one app (one war, etc), because It can simplify our deployment and development process.
This applications don't need to share beans or anything, should be completely separate. Both have DispatcherServlet and DispatcherFilter (and both are using Spring Security, but different configuration)
How I can configure web.xml for such webapp?
I've tried to add new filter:
<filter>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>extraSpringSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
and spring dispatcher servlet:
<servlet>
<servlet-name>extraSpring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>springConfigLocation</param-name>
<param-value>classpath:extra-spring-web.xml</param-value>
</init-param>
</servlet>
Where:
two context xml in classpath (inside exra library jar):
extra-spring-web.xml
extra-spring-security.xml (!!! how I should configure it?)
extra-spring-security.xml
is pretty standard Spring Security config
have configured bean extraSecurityFilterBean
have dependecy to beans from -web context (but it's not required to be)
It's semi-working now:
as I see from logs, extraSpring servlet successfully load beans from extra-spring-web.xml
but after accessing url /extra/ I got NoSuchBeanDefinitionException: No bean named 'extraSecurityFilterBean' is defined.
So, the question, how I can define context for DelegatingFilterProxy? I even tried to add this files into main context (contextConfigLocation param), it's not what i'm looking for, but it didn't work.
I've taken a look into DelegatingFilterProxy sources, but it's not clear for me how it loads the context.
As per my comment on the question, if the security filter chain is defined in extra-spring-security.xml then you need to ensure that that file is loaded by your extra DispatcherServlet in addition to extra-spring-web.xml either by <import>ing the -security file from the -web one or configuring it as:
<servlet>
<servlet-name>extraSpring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:extra-spring-web.xml
classpath:extra-spring-security.xml
</param-value>
</init-param>
</servlet>
You will also need to ensure that the security filter in the Grails application doesn't apply to /extra URIs, exactly how you do this depends on whether you're using annotations, database RequestMap entries etc.
If the modules are completely separate: the easiest way is to package them as two different webapp. Tens of different spring-based apps can run in one appserver -even on a modest developer machine- without issues.
A few questions
What does your Spring Security configuration look like?
I'm confused why the error states "No bean named 'apiservSecurityFilterChain' is defined" but the web.xml you have posted only references extraSpringSecurityFilterChain (the bean names should match or some important configuration is being left out).
Possible Answer
I'm guessing the problem is that the filter-name needs to match Spring Security's bean name (cannot know for sure without seeing the Spring Security configuration you are using). The default value used by the Spring Security namespace is springSecurityFilterChain, so try the following in the web.xml instead (notice extraSpringSecurityFilterChain changed to springSecurityFilterChain):
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.extraSpring</param-value>
</init-param>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>extraSecurityFilterBean</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/extra/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Categories