SpringBoot/Spring Cloud Config Client multiple application contexts (root & servlet) with RefreshScope annotated beans - spring-boot

I have a couple of questions regarding Spring Cloud Config in a web application.
Context
I'm working on an SpringBoot web application where I have a Root application context (parent) and a Servlet application context (child), I'm adding Spring Cloud Config capabilities to this application (of course this app connects to a Spring Cloud Config server via http).
When I annotate a bean that is registered in the Root application context with the RefreshScope annotation everything works as expected, the problems started when I tried to annotate a spring bean that is registered in the Servlet application context.
First problem: an IllegalStateException was thrown when the #RefreshScope annotated bean in the servlet application context was tried to be accessed, the following is the meaningful part of the stacktrace:
ERROR: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope name 'refresh'] with root cause
java.lang.IllegalStateException: No Scope registered for scope name 'refresh'
Solution: the error above was solved by registering the refresh scope to the Servlet application context
#Bean
public CustomScopeConfigurer servletCustomScopeConfigurer(org.springframework.cloud.context.scope.refresh.RefreshScope refreshScope) {
CustomScopeConfigurer customScopeConfigurer = new CustomScopeConfigurer();
customScopeConfigurer.addScope("refresh", refreshScope);
return customScopeConfigurer;
}
Question: Assuming that this way to model a web application is still relevant (I think it is), why I couldn't find any documentation stating that it's necessary to register the refresh scope to the servlet application context and I should register the org.springframework.cloud.context.scope.refresh.RefreshScope bean from the root application context?
Second problem: Once I had the refresh scope registered in the servlet application context the application started fine, and everything seemed to be working, however when I tried to update a property from the refreshable beans I had mixed results, when I refresh a property that would modify a bean in the root application context I could see the bean being updated, so all good, however when I update a property that would modify a bean in the servlet application context I could set the bean being refreshed but the new property value was not being reflected in the bean.
Solution: After some tests I realised that the ConfigurableEnvironment in both contexts are not the same, at some point the parent ConfigurableEnvironment is merged to the child ConfigurableEnvironment that is how beans in the servlet context get initialised correctly at startup, but when the ContextRefresher.refresh() is called by the RefreshEndpoint only the parent ConfigurableEnvironment is updated, and the child ConfigurableEnvironment is not leaving it with the old values, what I did now was to inject the parent ConfigurableEnvironment when creating the servlet application context and everything worked as expected, relevant code:
#Bean
public DispatcherServlet dispatcherServlet(ConfigurableEnvironment parentEnvironment) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(WebConfiguration.class);
applicationContext.setEnvironment(parentEnvironment);
dispatcherServlet.setApplicationContext(applicationContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
Question: Again I was surprised by the lack of documentation on the expected behaviour of a Spring application declaring parent and child application contexts, maybe I just didn't find it and it exists and is amazing. Is the approach of setting the parent ConfigurableEnvironment to the child servlet context the correct one?
In the default case where (parent ConfigurableEnvironment NOT set in the servlet context.) would be the fact that the child ConfigurableEnvironment is not refreshed if is intentional or a bug?
If you are still reading this, thanks! Answers to the questions above will be very appreciated!
Hopefully this helps other people dealing with these issues.

Related

Using both WebApplicationInitializer and web.xml in spring mvc+spring security+spring session redis web application

I'm trying to implement Spring redis session in an existing Spring MVC (ver 5.1.6) application. In web.xml we have ContextLoaderListener, DispatcherServlet and contextConfigLocation are all defined.
After required dependencies are included and suggested code changes are done, i'm getting below error:
Caused by: java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader definitions in your web.xml!"}}*
As part of code changes i'm extending the class "AbstractHttpSessionApplicationInitializer",(from Spring session core library) which internally implements WebApplicationInitializer. Seems like that is trying to create another context and throwing the above error. We cannot avoid extending this class, as this does the job of registering redisHttpSession to context.
Most of the examples available are all with spring boot. So there they wouldn't have faced this issue.
Any solution, other than completely replacing web.xml and use only WebApplicationInitializer?
Just want to provide an update. Instead of extending AbstractHttpSessionApplicationInitializer abtract class, i have taken a different approach by initializing bean RedisHttpSessionConfiguration thru XML bean definition.
This approach worked.
Followed the steps mentioned in the below thread;
How to configure Spring sessions to work with Redis in xml?
Along with that we need to serialize the cookie as well;
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("SESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}

Java Spring bean scopes: singleton vs application

Could anyone explain the difference between these two Spring bean scopes?
I'm familiar with the Singleton pattern.
Would this be the only difference?
You can have a list of beans in the Spring container using application scope.
Also, are you able to run multiple web servers in one Spring container? If yes, that would be a reason to use the application scope over the singleton scope since otherwise the bean would get shared over the two servers.
The documentation explains it:
This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per ServletContext, not per Spring 'ApplicationContext' (or which there may be several in any given web application), and it is actually exposed and therefore visible as a ServletContext attribute
In application scope, the container creates one instance per web application runtime.
The application scope is almost similar to singleton scope. So, the difference is
Application scoped bean is singleton per ServletContext however singleton scoped bean is singleton per ApplicationContext. It means that there can be multiple application contexts for single application.
SINGLETON SCOPED BEAN
//load the spring configuration file
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("context.xml");
// retrieve bean from spring container
MyBean myBean = context.getBean("myBean", MyBean.class);
MyBean myBean2 = context.getBean("myBean", MyBean.class);
// myBean == myBean2 - output is true.

Spring ServletContext bean (i.e. Controller) injection into ApplicationContext bean (i.e. Service, Dao)

In Spring, relationship between Application Context and Servlet Context is like parent-child. Application Context is the parent here. So, beans of Servlet Context know beans of Application Context, but vise versa is not true. But if I need to do that, is there any way ?
For simplicity, if I inject an Controller into an Service class, what I need to do ? Is there any Application Context refreshing mechanism, after Servlet Context is initialized ?

Spring, using new ClassPathXmlApplicationContext and getting error being unable to find applicationContext.xml and others?

I am trying to follow this tutorial: http://www.vogella.de/articles/SpringDependencyInjection/article.html to use annotation dependency injection in my application. I set up the bean, etc like in the tutorial and then am trying to get an instance of the bean within my MainController class (a controller class that handles generating a specific page for my spring web mvc app).. I keep getting
SEVERE: Servlet.service() for servlet spring threw exception
java.io.FileNotFoundException: class path resource [WEB-INF/applicationContext.xml] cannot be opened because it does not exist
I am doing this in my MainController:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanFactory factory = context;
BeanIRPlus beanirPlus = (BeanIRPlus) factory
.getBean("BeanIRPlus");
IRPlusInterface irPlus = beanirPlus.getIRPlus();
I have searched and searched on this and yet to find an answer that fixes my problem. My applicationContext in in webapp/WEB-INF/ and my spring app seems to be working otherwise as it was handling requests, etc before this. I have tried putting the applicationContext.xml in WEB-INF classes but still nothing. Is there any workaround to make this not search the path this way as I think its doing a relative path search. Thanks for any advice
Not a direct answer, but here goes.
The tutorial you have referred is for dependency injection in a standalone application and not a web application. In case of web application, spring automatically loads the context files and initializes the beans. So you would not need any of the lines specified in the MainController.
Instead, you could do something like this to use beanIRPlus bean in your controller.
#Autowired
private BeanIRPlus beanIRPlus;

Spring bean initialized on web app starts up

This is probably due to my ignorance of the Spring framewok but i am building a JSF+Facelets+Spring web app, i have a bean that whose init method i want to get called at the time the application is started. My problem is getting the bean initialized. I can reference the bean on a page, and when I go to the page, the bean is initialized, and works as directed; what I would like instead is for the bean to be initialized when the application is started
What is the way to get a Spring bean initialized on web app starts up
Your question is more Spring-targeted than JSF-targeted. I know the solution for JSF, but I don't think that this suits a Spring bean. I googled a second and found this topic at the Spring forum which may be of use for you. It describes/links several different approaches: http://forum.springsource.org/archive/index.php/t-21982.html
All the code that you want to handle immediately after an webapp is initialized can be done in a class that implements ServletContextListener as
#WebListener
public class ApplicationListener implements ServletContextListener {...}
and you can create spring application context like
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"applicationContext.xml", "applicationContext-part2.xml"});
// of course, an ApplicationContext is just a BeanFactory
BeanFactory factory = context;
and get the bean you are interested in and go on.

Resources