0

I have a spring application and there is a scheduled logic which produces prototype beans at fixed rate.

Everything works fine on my laptop but after deploying it to server my app fails to start due to:

2016-12-13 04:13:01.885 ERROR 4688 --- [TaskScheduler-1] o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task.
...
BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'metaDataSourceAdvisor': Singleton bean creation not allowed
 while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
...

This is the first scheduled call. Looks like a timing issue. On my laptop application initializes much faster and nothing wrong happens for the first scheduled call (which produces prototype beans).

Is there a way to get to know the current state of Spring bean creation flow so I can check it in scheduled logic and produce nothing when app creation is not finished?

Thanks!

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
  • You need to post the full callstack. Could you write more about the differences between you laptop environment and the server environment. Is the server a servlet/application-container and is it different than the one used for development? – Klaus Groenbaek Dec 13 '16 at 10:00
  • Hi, Klaus! My question is: Can I inject any spring core out-of-box bean which will tell me the current state of Spring application initialization in my scheduled logic? Because I don't want to produce prototype beans when app is not initialized – Nikolay Papakha Dec 13 '16 at 10:17
  • I have the comments on SO, so see the answer below. Basically context creation is single threaded. If you call ApplicationContext.getBean() make sure that the thread doing so is synchronized with the lifecycle of the spring context. If the thread is provided by a servlet-container or similar runtime, you can use AbstractApplicationContext.isActive() and .isRunning(), but as with all concurrent programming the context may be closed after you read the state, so to be safe you should only perform spring operation on threads managed by spring. – Klaus Groenbaek Dec 13 '16 at 10:53

1 Answers1

1

Looking at the Spring source (4.3.3 and 4.2.2) this only happens in one location DefaultSingletonBeanRegistry.getSingleton(), and only when the initialisation of the spring context has failed, or you have explicitly closed it. Spring uses a single thread (the calling thread) for creating your beans and invoking the start lifecycle methods, so the exception you found should only be possible if you violate this principle.

  • When you say scheduled task, do you mean scheduled by Spring (using @Scheduled), or do you have your own scheduling logic?
  • Do you start any threads in you application, and if so, does the bean owning the thread implement Spring's Lifecycle or SmartLifecycle interfaces?

I have seen a lot of developers that start threads in a bean constructor, this is a very bad idea. If you have a bean with lifecycle (thread, resource pool), you should always use the LifeCycle interface, and create your resource in start(), and clean it up in stop(). This is a good idea because start() is called after every eager singleton has been instantiated and the context completely wired, so no threads gets created unless every bean can be constructed.

Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
  • Thanks, Klaus! The exception happens in @Scheduled annotated method! And this method produces some prototype beans (which implement Runnable interface) and submit them to ExecutorService. I will try to follow your advice and implement LifeCycle interface! – Nikolay Papakha Dec 13 '16 at 10:29
  • Based on your info, I can only see this happening during shutdown of the spring context: @Scheduled should not be called until all singletons are created, and it will be called by a spring managed thread. Your ExecutorService lifecycle is not managed by spring, so it can call beanfactory.getBean() to get a prototype bean after spring has been shutdown, if the prototype bean autowires any singleton beans it would fail with BeanCreationNotAllowedException. Try making a bean that owns the ExecutorService, have it implement SmartLifeCycle. – Klaus Groenbaek Dec 13 '16 at 10:43
  • Klaus, thank you very much for rapid response and clear answer! SmartLifeCycle is exactly what I was looking for! – Nikolay Papakha Dec 13 '16 at 11:39
  • Glad to help. I think this is an area of Spring that many developers overlook (took me some time to find it), but it makes the code so much cleaner. I do a lot of code that needs to be continuously deployed on servlet-container, and you really need a well defined application lifecycle to deploy new applications on a running Tomcat (Tomcat automatically stops and undeploys the old version of the app when it is no longer used). – Klaus Groenbaek Dec 13 '16 at 12:18