9

I have spring application alongside (jersey 2.6 classes and ) servlets .

I need to get Spring bean(s) from jersey/non spring context,

Similar question suggested to get context in a static wrapper of context

public static ApplicationContext getContext() {
    return context;
}

How can I be sure the context is already loaded or not null?

If I can't, how should I wait/check until it spring context is loaded?

In case of calling from jersey context or calling bean from a simple HttpServlet code

EDIT

Jersey is working fine using jersey-spring3 dependency jar, so my question is only about Servlets out of Spring control

EDIT 2

The application is loading spring different than @entpnerd suggested article

It register a Servlet implementing a WebApplicationInitializer

public class MyWebAppInitializer implements WebApplicationInitializer {

But also have DispatcherServlet configured in web.xml

How can the DispatcherServlet loaded only after Spring loaded?

Because we add Autowiring capabilities on its init method:

WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext())
                    .getAutowireCapableBeanFactory().autowireBean(this);

Is adding a timeout before serving requests is the most prefer solution or is there a tweak in class loading that can take care of it?

EDIT 3

I found answers and answers of injecting, but not why Spring is loaded before Servlet.

Community
  • 1
  • 1
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
  • 1
    You sure you can't let Spring inject Jersey in your case? This is a nice article explaning how to work with Jersey in Spring Boot. It's old, but should still be relevant. https://geowarin.github.io/a-simple-spring-boot-and-jersey-application.html – Gorazd Rebolj Nov 22 '17 at 13:11
  • Thanks for suggestion, I use jersey 2.6 and not 3, and I prefer a solution that include getting bean from HTTPServlet also – Ori Marko Nov 22 '17 at 13:14
  • The Spring way would be to let the Spring context initialize itself and only after start everything else. If you wonder whether Spring app context is loaded when you need it, you should review your booting procedure. – Serge Ballesta Nov 27 '17 at 08:44
  • Under tomcat 8, My Servlets are defined in web.xml while Spring is AnnotationConfigWebApplicationContext – Ori Marko Nov 27 '17 at 08:46

2 Answers2

5

The idea is quite simple, although the actual implementation may vary depending on an exact way of Spring boot and Jersery initialization.

An idea:

Spring boot, as being a purely runtime framework, is all about proper loading the application context (from the question standpoint).

So, bottom line, when it's loaded there is an application context somewhere in memory, and its possible to access beans from this application context.

Now, since you say that Jersey is not spring/spring-boot driven, this application context has to be reachable from some kind of static global variable by Jersey, it's quite ugly but should work.

So the idea has two steps:

  1. Put an application context reference to some static holder accessible from Jersey.
  2. Read this value in some infrastructure level code from Jersey component.

A Possible Implementation

Technically step one can be done by implementing some kind of spring boot listener that will store application context in some kind of singleton:

enum ApplicationContextHolder {
   INSTANCE;
    private ApplicationContext ctx;
    void setApplicationContext(ApplicationContext ctx) {
        this.ctx = ctx;
    }

    ApplicationContext getCtx() {
        return this.ctx;
    }

}


// and a listener (spring boot provides many ways to register one, but the 
// implementation should be something like this):
// The main point is that its managed by spring boot, and hence and access to 
// the application context
class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {           
             ApplicationContextHolder
             .INSTANCE
             .setApplicationContext(event.getApplicationContext());
    }
}

Now the step 2 is:

class MyJerseyOrWhateverComponentThatWantsToAccessApplicationContext {

    public void foo() {
       ApplicationContext ctx = ApplicationContextHolder.INSTANCE.getCtx();
       ... 
       ctx.getBean(...);
    }
}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • How can I be sure that step 1 is done before I get request to servlet? – Ori Marko Sep 03 '18 at 06:58
  • In general you can't, so the implementation of the holder can vary depending on your actual needs. Do you want the Jersey Servlet to "wait" till the app context starts? do you want to fallback to some initial behaviour if the application context is not available? You can implement both ways by slightly modifying the code of that singleton. Its hard to tell more, because both "processes" (jersey initialization) and spring boot initialization work completely independently – Mark Bramnik Sep 03 '18 at 07:06
  • Notice my question (edit) is for standalone servlets . do I **need** to wait? is there an option to ensure spring context is loaded before servlets can receive request and is it a valid solution? – Ori Marko Sep 03 '18 at 07:52
  • It actually depends how exactly you start the spring boot application. WebApp has its own lifecycle (and as I see it from your question) Jersey is bound to this lifecycle, so there should be some kind of listener. Now the question is how does Spring boot actual load up. If its also in a listener, then you can combine them so that Spring boot will load first (it loads synchronously) and only after that the jersey filter will execute. This way you guarantee that by the time the jersey servlet starts, application context is already available – Mark Bramnik Sep 03 '18 at 08:28
0

So a viable solution for this could happen in two stages:

  1. A Spring bean gets the ApplicationContext instance and sends it to a static singleton outside of the Spring context.
  2. Your standalone servlet gets the ApplicationContext instance from the static singleton and verifies that the right beans have been loaded.

Consider the following code as an example:

SpringMetaBean.java

// @Component so that it's part of the Spring context
// Implement ApplicationContextAware so that the ApplicationContext will be loaded
// correctly
@Component
public class SpringMetaBean implements ApplicationContextAware {
  private ApplicationContext appCtx;
  public setApplicationContext(ApplicationContext appCtx) {
    this.appCtx = appCtx;
  }

  // @PostConstruct so that when loaded into the Spring context, this method will
  // automatically execute and notify ApplicationContextHolder with a reference to
  // the ApplicationContext
  @PostConstruct
  public void setup() {
    ApplicationContextHolder.set(this.appCtx);
  }
}

ApplicationContextHolder.java

public class ApplicationContextHolder {
  // ensure the reference is thread-safe because Spring and standalone Servlet will
  // probably be running on different threads.
  private final AtomicReference<ApplicationContext> appCtxContainer = new AtomicReference<>();

  public void set(ApplicationContext appCtx) {
    this.appCtxContainer.set(appCtx);
  }

  public ApplicationContext get() {
    return this.appCtxContainer.get();
  }
}

MyStandaloneServlet.java

public class MyStandaloneServlet {
  // my request handler method
  public void getResponse(HttpServletRequest rq) {
    ApplicationContext springAppCtx = ApplicationContextHolder.get();
    // if not null, we know that Spring has been loaded and we can dig into the
    // application context.
  }
}
entpnerd
  • 10,049
  • 8
  • 47
  • 68
  • How do I ensure `ApplicationContextHolder` loaded before `MyStandaloneServlet` first request? – Ori Marko Sep 04 '18 at 06:11
  • Since `ApplicationContextHolder` is outside the Spring context, then it should be loaded whenever the first `set()` and `get()` calls happen, since Java lazy loads these classes. However, this first call to the `set()` method should happen before the first request - when Spring is loaded based on your `web.xml` configuration and `SpringMetaBean` calls the `set()` method as part of the loading of the Spring and web containers. – entpnerd Sep 04 '18 at 14:50
  • How/Why Spring loaded before Servlet in web.xml? – Ori Marko Sep 05 '18 at 04:46
  • The `web.xml` file is usually used using the dispatcher servlet. See https://www.baeldung.com/spring-xml-vs-java-config for an example. – entpnerd Sep 05 '18 at 04:51
  • I added more info in question regarding the article – Ori Marko Sep 05 '18 at 05:33