Search code examples
javaspringwar

Is it possible to locate a Spring non-web context from a web context?


I have a Spring app which launches a REST Service within an embedded instance of Jetty, which itself is launched from Spring.

The initial Spring context has an integration and database layer, and launches the Jetty instance. Jetty then calls its own application context file which exposes the REST service.

I would like to know if there's some way of exposing the initial Spring context to the web context run from within Jetty. Unfortunately I can't deploy a full J2EE server, and I'm hesitant to run everything from the Web context, relying on Jetty to manage threading and such.


Solution

  • So I found yet a better answer based off the one from ericacm above. The only reason that it's better is that you can still use <load-on-startup> for the servlets in the web.xml file.

    When embedding the Jetty server, you need to create a WebAppContext. The super-class ContextHandler lets you set an array of EventListener which includes ServletContextListener.

    So the solution is extend ContextLoader and implement both Spring's ApplicationContextAware and the ServletContextListener interface. The loader lets you return the parent context set by the contextaware interface, and the listener provides you the ServletContext via contextInitialized().

    You can then initialize this before any of the Jetty components, and get access to the fully populated ServletContext as the Jetty server is loading, which gets called before any of the web app's themselves are initialized.

    Listener implementation:

    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.web.context.ContextLoader;
    
    public final class EmbeddedJettySpringContextLoaderListener 
        extends ContextLoader 
        implements ApplicationContextAware,
                   ServletContextListener
    {
        private ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        /**
         * Returns the parent application context as set by the
         * {@link ApplicationContextAware} interface.
         * 
         * @return The initial ApplicationContext that loads the Jetty server.
         */
        @Override
        protected ApplicationContext loadParentContext(ServletContext servletContext) {
            return this.applicationContext;
        }
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            super.initWebApplicationContext(sce.getServletContext());
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            //not needed
        }
    }
    

    Jetty config for WebAppContext (eventually referenced by the server):

    <!-- Loads this application context as the parent of the web application context. -->
    <bean id="parentContextLoaderListener" class="com.citi.matrix.server.restapi.EmbeddedJettySpringContextLoaderListener" />
    
    <bean id="restApiWebAppContext" class="org.mortbay.jetty.webapp.WebAppContext">
      <property name="displayName" value="RestApi" />
      <property name="contextPath" value="/" />
      <!-- the value for war, must be a relative path to the root of the application, and does not use the classpath. -->
      <property name="war" value="${WEBAPPS_HOME}/rest-api" />
      <property name="eventListeners">
        <ref  local="parentContextLoaderListener" />
      </property>
      <property name="configurationClasses">
        <list>
          <value>org.mortbay.jetty.webapp.WebInfConfiguration</value>
          <value>org.mortbay.jetty.webapp.WebXmlConfiguration</value>
          <value>org.mortbay.jetty.webapp.JettyWebXmlConfiguration</value>
        </list>
      </property>
      <property name="logUrlOnStart" value="true" />
    </bean>