Singleton spring bean is being created more than once - java

I have a singleton spring bean named gameContext, my spring bean definition;
<bean name="gameContext" scope="singleton"
class="tr.com.hevi.game.numblock.core.context.GameContext"/>
I also use this class for session listening, here is my web.xml
<listener>
<listener-class>
tr.com.hevi.game.numblock.core.context.GameContext
</listener-class>
</listener>
The problem is that gameContext is being created twice. one; at the very beginning before the spring context is being loaded, and the second; within the spring context.
I am sure that I do not component-scan more than once.
I understand the reason behind but don't know how to tackle the problem. One possible solution should be adding the listener within the spring context not web.xml, or there might be a proxy object solution.

In your problem, there are 2 objects for spring because you are configuring the listener twice
The first is in the web.xml (outside the spring context)
Within the spring context as a bean.
The easiest way to have only 1 instance is if you are using the Servlet 3.0 specification. Here the ServletContext has a addListener() method make use of the same. Do something like the below:
#Component
public class MyCustomListener implements javax.servlet.http.HttpSessionListener, ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (applicationContext instanceof WebApplicationContext) {
((WebApplicationContext) applicationContext).getServletContext().addListener(this);
} else {
//Either throw an exception or fail gracefully, up to you
throw new RuntimeException("Must be inside a web application context");
}
}
}
The above approach will cause you to create only 1 object of the listener, and have the same object registered as a Servlet listener and spring bean.

Related

Spring autowire a class on server startup

I have a spring application. I am autowiring classes and they are working fine.
For e.g
#Controller
public class SearchController {
#Autowired
private EnvironmentControl envControl;
#Autowired
private SearchControl searchControl;
...
But now i have on server startup class called ScheduleServlet which uses init method to schedule something...
public class SchedulerServlet extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.LOGGER.info("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager) ic.lookup("java:comp/env/tm/TimerManager");
Timer timer = tm.schedule(new GlobalTemplateScheduler(), 0, 3600000);// one hour interval
System.out.println("Timer..... " + timer);
}
...
In this my GlobalTemplateScheduler class has timerExpired method which is scheduled to execute after every one hour interval.
public class GlobalTemplateScheduler implements TimerListener {
#Autowired
private TemplateControl templateControl;
#Override
public void timerExpired(Timer timer) {
try {
templateControl.updateMappings(names);
} catch (Exception e) {
this.LOGGER.error(e.getMessage());
e.printStackTrace();
}
...
So i have to autowire templateControl which i am getting null. This should happen on server startup.
Further inside updateMappings there's a datasource object which is also autowired as constructor-arg(This is working fine on browser request but need to do it on server startup).
Note: I cannot use the ApplicationListener interface.
Any suggestions would really help.
Thankyou.
On application startup beans initialization would not be completed, beans can be used after the application context refresh or after the intialization of the bean, it will make no sense to execute a logic which requires the bean on the startup unless you detect whether the bean is ready or not.
You can execute some logic using #PostConstruct in the bean which will be executed after the initialization of the bean so you can manipulate your logic in a way to do so after the intialization of the bean or you could detect and execute logic after the ContextRefreshedEvent by impelementing applicationListener and put your logic in onAppplicationEvent method.
One solution would be to use Spring's Container within your servlet. There are many implementations for this purpose, for instance the AnnotationConfigApplicationContext. Spring's documentation describes how to use the ClassPathXmlApplicationContext
Suppose GlobalTemplateScheduler is also a bean, then the key point is this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
GlobalTemplateScheduler sheduler = context.getBean("sheduler", GlobalTemplateScheduler.class);
sheduler.someMethod();
The content of the XML, which is used by the ClassPathXmlApplicationContext is small. But you need to enable component scan:
<context:component-scan base-package="foo.bar.baz" />
Another approach, I could suggest, is to use Spring's DispatcherServlet to wire all the beans together. It can use the same XML, it is just a matter of loading it. The benefit is that is you don't need to load the application context by yourself and launch a bean as an entry point
There are plenty of tutorials how to use this servlet.
If you dont't like to write XML, you could use the WebApplicationInitializer
As i said the beans which i was autowiring were working fine. I just needed those beans in my Scheduler Servlet.
Here's the solution which worked...
In my scheduler servlet i got the application context xml and used the beans which were required...
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
GlobalTemplateControl globalTemplateControlObject = context.getBean("globalTemplateControl", GlobalTemplateControl.class);
Thanks #duffymo #Amer Qarabsa #Spindizzy for your suggestions :)

Spring MVC: autowire ignored in servlets

For some reasons, I can autowire in my controllers but not in a servlet I created.
This is the top part of my servlet:
#Component
public class MyServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
#Autowired
private CobiService cobiService;
In my web.xml this is the related configuration:
<servlet>
<servlet-name>convservlet</servlet-name>
<servlet-class>com.gim.servlets.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>convservlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
And this is the way I tell spring to scan for components:
<context:component-scan base-package="com.gim" />
For some reason my autowired object cobiService is null. Did I forget anything? What should I change?
Servlets are not managed by Spring, they are managed by the Servlet container (like Tomcat). Therefore Spring cannot inject dependencies into Servlets in a normal Spring way. You can however do something like the following:
public class MyServlet extends javax.servlet.http.HttpServlet {
private CobiService cobiService;
#Override
public void init() throws ServletException {
super.init();
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
cobiService = applicationContext.getBean(CobiService.class);
}
}
You can override your servlet init method, and do
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext (this);
SpringBeanAutowiringSupport
There is no need to do
implements javax.servlet.Servlet, since you are extending HttpServlet
There are two servlets being created. One is being created by the servlet container/Java EE container when the application initializes and reads the configured <servlet> tag inside the web.xml file. The other would be created when the Spring IOC container performs its component scanning upon initialization.
In this case, the first instance is not able to participate in dependency injection because it was not created within the Spring IOC container. In order to participate in dependency injection, a bean must be managed by the Spring IOC container. When the servlet container/Java EE container instantiates the servlet it has no knowledge of Spring's IOC container.
Unfortunately, when a request comes in that satisfies the url-pattern specified in the web.xml file for the servlet, the request is redirected to the first instance created by the servlet container/Java EE container which is not a bean and not a candidate for autowiring.
If you were to remove the servlet from web.xml and add a #RequestMapping annotation to the servlet, the second instance (which is an actual bean able to utilize autowiring) would be utilized for requests fullfilling the specified url pattern in the #RequestMapping, however at that point you pretty much have a controller.
So in summary, my recommendation would be to adhere to Spring conventions and use a controller class. The controller class will match and exceed the functionality provided by the Servlet.

Spring root application context and servlet context confusion

I know that I need to register classes annotated with #Controller in my servlet context to make my webapp accesible. Usually, I will do it the following way:
#Configuration
#EnableWebMvc
#ComponentScan({"foo.bar.controller"})
public class WebConfig extends WebMvcConfigurerAdapter {
//other stuff like ViewResolvers, MessageResolvers, MessageConverters, etc.
}
All other configuration classes I added to my root application context. Here is how my dispatcher initializer usually look like:
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RootConfig.class, ServiceConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
But things are getting more interesting when I started to use WebSockets. To get websockets working, you have to put WebSoketConfig.class to servlet context. Here is my example of WebSocketConfig:
#Configuration
#EnableScheduling
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
#Override
public void configureClientInboundChannel(ChannelRegistration channelRegistration) {
channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8);
}
#Override
public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {
channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue", "/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
Also, I've created a service to send a message to the topic:
#Service
public class TimeServiceWsImpl implements TimeServiceWs {
#Autowired
private SimpMessagingTemplate messagingTemplate;
#Override
public void sentCurrentTime() {
long currentTime = System.currentTimeMillis();
String destination = "/topic/chatty";
logger.info("sending current time to websocket /topic/time : " + currentTime);
this.messagingTemplate.convertAndSend(destination, currentTime);
}
}
I need to use this service in some other services (Autowire it). And now I'm in a deadlock:
If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException
If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service, because root context can't see servlet context beans(as far as I know)
If I move all my configurations to servlet context, all beans are successfully created, but I get the following exception: java.lang.IllegalStateException: No WebApplicationContext found and can't access my webapp
What am I supposed to do? What should be inside root context? What should be inside servlet context? And could you please clarify the difference between these context one more time please?
If you will need any additional information, just let me know.
Most Spring MVC applications have one root context containing all service layer / DAO layer beans, and one servlet context per spring dispatcher servlet of the application, which contains (at least) the controllers of each servlet.
The idea being that is that one application might have several servlet dispatchers, for example one for URL /shopping/* and the other for URL /reporting/*, each with it's own set of controllers.
The controllers of one servlet dispatcher are isolated from each other, meaning although they are also Spring beans, they cannot be injected in each other.
Service layer and DAO beans in the root context are visible in all servlet contexts, so Service layer beans can be injected in any controller, but not the other way around.
The root context is said to be the parent of the controller servlet context/contexts.
It's all meant to be a mechanism of isolating groups of beans from each other to ensure no unmeant dependencies are possible.
Given this and going through the questions:
If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException: Move the SimpleMessagingTemplate to the root context, it's a bean like a DAO that can be useful anywhere in the application so it should be in the shared root context.
If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service: If it's meant to be autowired to other services, leave it in the root context then.
- If I move all my configurations to servlet context, all beans are successfully created, but I get java.lang.IllegalStateException: No WebApplicationContext found: Do the opposite, move basically all beans to the root context, and leave on the servlet context only the beans that are specific of that part of the application, many times only the controllers.
WebSocket-related config belongs to the DispatcherServlet configuration one way or another. After all the HTTP handshake is processed by the DispatcherServlet through its handler mappings.
You should be able to go with a single Spring context in a deployment scenario where there is only one DispatcherServlet in the web application. Consolidating the configuration into the root context makes more sense if using Spring Security for example although there was a bug with the AbstractAnnotationConfigDispatcherServletInitializer (see SPR-11357). Consolidating into the DispatcherServlet context should also be possible but you wrote that you got exceptions. Can you provide the exception details?
It is also an option to have both root and DispatcherServlet contexts. In that case the WebSocket configuration will be in the DispatcherServlet context and it's not possible to inject the SimpMessagingTemplate into beans in the root context. That actually makes sense since there is one SimpMessagingTemplate to go with each DispatcherServlet (or some other servlet). What's needed is a web layer component, perhaps a thin wrapper around service layer beans (like TimeServiceWs the above example) that can also be injected with the SimpMessagingTemplate. This web layer component essentially serves as a bridge.

Spring singleton created multiple times

I have a bean defined in my spring web application and I am expecting to have only one instance of this bean, here is my bean definition:
<bean id="accessControl" class="my.spring.app.AccessControl" />
In the constructor of AccessControl, I assign an identifier to the object, something like this:
public class AccessControl {
private long id = 0;
public AccessControl() {
id = System.currentTimeMillis();
}
public long getAccessControlId() {
return id;
}
}
In a different class, I try to get hold of the instance of AccessControl, something like this:
ApplicationContext ctx =
new ClassPathXmlApplicationContext("acbean.xml");
AccessControl ac = (AccessControl) ctx.getBean("accessControl");
LOGGER_.info("AccessControl Identifier : " + ac.getAccessControlId());
I am expecting the "id" value to be the same because the value of "id" is set in the constructor and constructor should not get called again and again but that is exactly what is happening. Infact, I added a log statement to the constructor and a new object is created everytime.
I have read: http://www.digizenstudio.com/blog/2006/09/14/a-spring-singleton-is-not-a-singleton/
but I don't think I am dealing with the same class defined twice with two different bean identifiers and the application context is the same.
Can anyone share what is wrong with the way I have defined the bean?
I have also experimented with singleton="true" and scope="singleton" but they do not make any differece.
Thanks.
the singelton-ness in spring is per application context, every time you create a new instance of the application context (like the first line in your second code sample) all the singletons are instantiated.
You need to have a single application context and reuse it around in your application
You are creating a new application context with each call of:
ApplicationContext ctx = new ClassPathXmlApplicationContext("acbean.xml");
So, you end up with a new spring container, which means that your beans are all re-created by the new container.
Also, you mentioned that you had this in a web application. If so, you need to allow the web application to load the spring context and to obtain and use that context as necessary.
Add to web.xml:
<context-param>
<description>Core Spring context.</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<listener>
<description>Spring loader.</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
or something similar. Obtain the web context via a servletcontext as needed.
One other note: one point of spring is to provide inversion of control, commonly with dependency injection. You should consider allowing spring to inject any dependencies for you rather than obtaining the context and pulling beans yourself.
In a Spring app you should not be explicitly creating an application context of your own.
Ideally the singleton should be injected into your class, or you should be implementing ApplicationContextAware (docs, and some notes). I prefer injection; easier.

Spring: Accessing the correct WebApplicationContext with multiple dispatchers declared

I have two Spring contexts declared in my application - one for Spring-MVC requests, and another for Flex/BlazeDS messagebroker requests, mapped to different url-patterns:
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>flex</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
There's a common context configuration declared, (/WEB-INF/applicationContext.xml) and then each of the two contexts have their own configurations declared in spring-mvc-servlet.xml and flex-servlet.xml respectively.
Inside flex-servlet.xml I have beans declared which are specific to the flex context. However, when a call comes in to http://localhost/messagebroker/* I'm getting errors that those beans aren't available.
The code in question is inside a custom Spring component, so directly references the WebApplicationContext in order to access the declared beans:
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
ServletContext ctx = FlexContext.getServletContext();
WebApplicationContext springContext = WebApplicationContextUtils.getRequiredWebApplicationContext(ctx);
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
This approach works when I'm running with a single context. However it needs to also support where there are multiple contexts running.
Setting a breakpoint, I see that the value of springContext is the root context, with a single configLocation - /WEB-INF/applicationContext.xml
I'm asssuming that this is the problem - as the ISerializer that the above code requires is declared in flex-servlet.xml.
How do I modify the above code to support both scenarios? (Single context, and multiple contexts)?
EDIT:
The code shown above sits inside a ManageableComponentFactoryBean, which appears to operate as a custom bean factory. It seems that the ApplicationContextAware interface is not honoured on generated classes. Eg:
<bean id="dpHibernateRemotingAdapterComponentFactory"
class="org.springframework.flex.core.ManageableComponentFactoryBean">
<constructor-arg
value="org.dphibernate.adapters.RemotingAdapter" />
<property name="properties">
<value>
{"dpHibernate" :
{
"serializerFactory" : "org.dphibernate.serialization.SpringContextSerializerFactory"
}
}
</value>
</property>
</bean>
The code quoted above sits inside the org.dphibernate.serialization.SpringContextSerializerFactory. Making this SpringContextSerializerFactory implement ApplicationContextAware has no impact.
If flex is a DispatcherServlet, and for some reason you can't follow Tomás Narros's suggestion, you can obtain a context associated with the current DispatcherServlet using RequestContextUtils.getWebApplicationContext(request).
There is also a convenience method RequestContextUtils.getWebApplicationContext(request, ctx), which returns the root context if DispatcherServlet's one is not available.
Declare your custom componente as Spring Context aware:
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public MyCustomBean implements ApplicationContextAware {
private ApplicationContext springContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
springContext = applicationContext;
}
public ISerializer getSerializer(Object source,boolean useAggressiveSerialization)
{
String serializerBeanName = springContext.getBeanNamesForType(ISerializer.class);
}
}
At the bean initialization, Spring will access the setApplicationContext method of your bean, passing as an argument the context in wich it's being created. There, you can keep it an use it whenever you need.
Hrmmmm.....I have nearly that exact sort of declaration in my Spring/Flex app, using Spring/Flex integration and there is only one application context. Could that be the problem? You have beans declared in the Flex context file that aren't in the MVC context file, and they aren't really getting loaded?

Categories

Resources