jersey-spring3 instantiating Spring-managed bean (null!)

后端 未结 3 1667
情歌与酒
情歌与酒 2020-12-06 02:26

I first want to point out that this is by and large the biggest problem in terms of time wasted that I have ever dealt with in my career. (Over two days straight now with e

3条回答
  •  旧巷少年郎
    2020-12-06 02:48

    We have a custom, asynchronous ContextLoader, so the interim solution required placing a total hack in the Jersey-Spring3 source to wait for the application to initialize before the custom component provider initializes.

    P.S. For any poor soul who finds themselves having to do something like this, make sure META-INF/settings contains the SpringComponentProvider configuration.


    (2014-04-18) Elaborating for @Scott

    Note that this is a terrible hack and I would only attempt such a thing as a last resort when all other attempts have failed, like in my case. Also I would consult the Jersey mailing group about your problem before attempting anything like this.

    That said... this is what I did to solve my problem:

    • Literally copied the source code of spring-jersey3 into my application/server, modifying the header of every file with the appropriate tags as per the license;

    • Created the following class --

    ===>

      /**
       * Hack class for RN-8979.
       *
       * @author ryan
       *
       */
      public class ContextLoadWaiter {
    
        private static final Logger logger = Logger.getLogger(ContextLoadWaiter.class);
    
        public void doWait() {
    
          try {
            while (ContextLoaderHttpInterceptor.isNotStarted()) {
              logger.info("Waiting for ContextLoader to start...");
              Thread.sleep(1000);
            }
          } catch (InterruptedException e) {
            logger.error("SpringComponentProvider was interrupted!");
          }
        }
      }
    

    Note that this is specific to *our* code-base as ContextLoaderHttpInterceptor is an http servlet where isNotStarted returns true if our custom ContextLoader (which happens to be asynchronous) is not yet loaded.

    The custom asynchronous ContextLoader was put in place sometime by somebody for some reason along the lines of allowing the UI to display a "loading" page while the server boots up. (Probably not the correct way to add this UI "feature", but the code was there and the UI depended on it, so I had to deal with it...)

    Since this part will not apply directly to you, the key thing is to debug through SpringComponentProvider (from here) and look at the value of the ClassPathXmlApplicationContext. If it is null, as it is in our case, then you need to figure out why it is null and wait on whatever ContextLoader you use to load before you initialize this component.

    • Placed this hacky line in SpringComponentProvider --

    ==>

      ...
    
      private final ContextLoadWaiter waiter = new ContextLoadWaiter();
    
      ...
    
      @Override
      public void initialize(ServiceLocator locator) {
    
        waiter.doWait(); // Wait on our asynchronous context loader.
    
        this.locator = locator;
    
        if (LOGGER.isLoggable(Level.FINE)) {
          LOGGER.fine("Context lookup started");
        }
    
        ...
    
    • Created this file: META-INF/services/org.glassfish.jersey.server.spi.ComponentProvider with the contents being the fully qualified classpath to the SpringComponentProvider, e.g. com.company.server.nbi.rest.internal.jspring.SpringComponentProvider

    • Added the custom Jersey-spring3 package as a package to scan in the application; see below...

    ==>

    /**
     * Application configuration.
     *
     * @author ryan
     *
     */
    public class MyJerseyApplication extends ResourceConfig {
    
      private static final class Messages {
        static final String INF_STARTING_APPLICATION = "Starting %s!";
      }
    
      private static final Logger logger = Logger.getLogger(MyJerseyApplication.class);
    
      public MyJerseyApplication() {
    
        packages(
        /* Internal providers */
        "com.company.server.nbi.rest.providers",
        /* Internal filters */
        "com.company.server.nbi.rest.filters",
        /* Spring injection support */
        "com.company.server.nbi.rest.internal.jspring", // HERE!!!
        /* Json providers */
        "com.fasterxml.jackson.jaxrs.json",
        /* Jackson exception mappers */
        "com.fasterxml.jackson.jaxrs.base");
    
        /* Resources */
        register(ResourceA.class);
        register(ResourceB.class);
        register(ResourceC.class);
    
        /* Miscellaneous features */
        register(MultiPartFeature.class);
        register(LoggingFilter.class);
    
        logger.info(format(Messages.INF_STARTING_APPLICATION, this.getClass().getName()));
      }
    }
    

    That's "it". Definitely not a solution to be proud of, but if you are in desperation mode like I was, it probably doesn't hurt to give it a shot.

提交回复
热议问题