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.
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>