3

The cool enterprise app I'm working on is in the process of going Spring. That's very cool and exciting exercise to all the team, but also a huge source of stress. What we do is we gradually move legacy components to Spring context. Now what we have is a huuuge, I mean it, huuuuge component that is not piece of cake to spring-ify, and at the same time it needs to get access to some of the Spring beans.

Now here comes the problem: this component is being loaded at application startup (or bootstrap, whatever you prefer!). That means that there is a race condition between this guy and a Spring itself, so sometimes when I access the context from within that non-spring monstrosity, I get sweet and nice NPE. Which basically means that at the time we need that context, it's not yet initialized!

You might be curious how exactly we're accessing the context: and the answer is - it's a standard AppContextProvider pattern.

public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext ctx;


    public void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }

}

The ideal workaround for me in this case would be to tell Spring to notify that non-spring component "Okay, I'm up!", and perform all actions that require the context only after that. Is this actually possible?

Thanks in advance!

Alexey Buistov
  • 209
  • 2
  • 4
  • 11

2 Answers2

2

The correct way to make the application context available to non-spring beans is to use the ContextSingletonBeanFactoryLocator.

Take a look at this answer for more details.

Community
  • 1
  • 1
gkamal
  • 20,777
  • 4
  • 60
  • 57
1

Take a look at the mechanism of context events.

Perhaps you can block getApplicationConext() until receiving of ContextRefreshedEvent (if it wouldn't create deadlocks):

public class ApplicationContextProvider implements ApplicationListener<ContextRefreshedEvent> {

    private static ApplicationContext ctx;
    private static Object lock = new Object();

    public void onApplicationEvent(ContextRefreshedEvent e) {
        synchronized (lock) {
            ctx = e.getApplicationContext();
            lock.notifyAll();
        }
    }

    public static ApplicationContext getApplicationContext() {
        synchronized (lock) {
            while (ctx == null) lock.wait();
            return ctx;
        }
    }
}
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • Please correct me if I'm wrong, but the way this context events mechanism works is by declaring all interested parties in the app context, which is impossible in my case. Let me reiterate that my service is completely non-spring and there is no quick way to make it a spring bean. I'd like to have some sort of a communication bridge between it and a Spring container, if such feature exists in Spring. – Alexey Buistov Dec 09 '11 at 09:54
  • @Alex: But your `ApplicationContextProvider` is declared in the Spring context. It can listen for events, and communicate with the third parties accordingly. – axtavt Dec 09 '11 at 09:58
  • ApplicationContextProvider pattern works fine UNLESS you need the Spring context during the app startup. In this case, as I stated in the original post, a race condition can occur. – Alexey Buistov Dec 09 '11 at 10:13
  • Looks interesting. Thanks, I'll try this and submit my progress report here :) – Alexey Buistov Dec 09 '11 at 10:31