*/
package org.opendaylight.aaa.web.jetty;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.EnumSet;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
import javax.inject.Singleton;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.opendaylight.aaa.web.WebContext;
import org.opendaylight.aaa.web.WebContextRegistration;
import org.opendaylight.aaa.web.WebServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* {@link WebServer} (and {@link WebContext}) implementation based on Jetty.
* @author Michael Vorburger.ch
*/
@Singleton
-// @SuppressWarnings("checkstyle:IllegalCatch") // Jetty LifeCycle start() and stop() throws Exception
+@SuppressWarnings("checkstyle:IllegalCatch") // Jetty LifeCycle start() and stop() throws Exception
public class JettyWebServer implements WebServer {
- public void stop() {
+ private static final Logger LOG = LoggerFactory.getLogger(JettyWebServer.class);
+
+ private static final int HTTP_SERVER_IDLE_TIMEOUT = 30000;
+
+ private final int httpPort;
+ private final Server server;
+ private final ContextHandlerCollection contextHandlerCollection;
+
+ public JettyWebServer(int httpPort) {
+ checkArgument(httpPort > 0, "httpPort must be positive");
+ checkArgument(httpPort < 65536, "httpPort must < 65536");
+
+ this.httpPort = httpPort;
+ this.server = new Server();
+ server.setStopAtShutdown(true);
+
+ ServerConnector http = new ServerConnector(server);
+ http.setHost("localhost");
+ http.setPort(httpPort);
+ http.setIdleTimeout(HTTP_SERVER_IDLE_TIMEOUT);
+ server.addConnector(http);
+
+ this.contextHandlerCollection = new ContextHandlerCollection();
+ server.setHandler(contextHandlerCollection);
}
@Override
- public WebContextRegistration registerWebContext(WebContext webContext) throws ServletException {
- throw new UnsupportedOperationException("TODO Implement!");
+ public String getBaseURL() {
+ return "http://localhost:" + httpPort;
+ }
+
+ @PostConstruct
+ @SuppressWarnings("checkstyle:IllegalThrows") // Jetty WebAppContext.getUnavailableException() throws Throwable
+ public void start() throws Throwable {
+ server.start();
+ LOG.info("Started Jetty-based HTTP web server on port {} ({}).", httpPort, hashCode());
+ }
+
+ @PreDestroy
+ public void stop() throws Exception {
+ LOG.info("Stopping Jetty-based web server...");
+ // NB server.stop() will call stop() on all ServletContextHandler/WebAppContext
+ server.stop();
+ LOG.info("Stopped Jetty-based web server.");
}
@Override
- public String getBaseURL() {
- throw new UnsupportedOperationException("TODO Implement!");
+ public synchronized WebContextRegistration registerWebContext(WebContext webContext) throws ServletException {
+ ServletContextHandler handler = new ServletContextHandler(contextHandlerCollection, webContext.contextPath(),
+ webContext.supportsSessions() ? ServletContextHandler.SESSIONS : ServletContextHandler.NO_SESSIONS);
+
+ // The order in which we do things here must be the same as
+ // the equivalent in org.opendaylight.aaa.web.osgi.PaxWebServer
+
+ // 1. Context parameters - because listeners, filters and servlets could need them
+ webContext.contextParams().entrySet().forEach(entry -> handler.setAttribute(entry.getKey(), entry.getValue()));
+ // also handler.getServletContext().setAttribute(name, value), both seem work
+
+ // 2. Listeners - because they could set up things that filters and servlets need
+ webContext.listeners().forEach(listener -> handler.addEventListener(listener));
+
+ // 3. Filters - because subsequent servlets should already be covered by the filters
+ webContext.filters().forEach(filter -> {
+ FilterHolder filterHolder = new FilterHolder(filter.filter());
+ filterHolder.setInitParameters(filter.initParams());
+ filter.urlPatterns().forEach(
+ urlPattern -> handler.addFilter(filterHolder, urlPattern, EnumSet.allOf(DispatcherType.class))
+ );
+ });
+
+ // 4. servlets - 'bout time for 'em by now, don't you think? ;)
+ webContext.servlets().forEach(servlet -> {
+ ServletHolder servletHolder = new ServletHolder(servlet.name(), servlet.servlet());
+ servletHolder.setInitParameters(servlet.initParams());
+ servletHolder.setInitOrder(1); // AKA <load-on-startup> 1
+ servlet.urlPatterns().forEach(
+ urlPattern -> handler.addServlet(servletHolder, urlPattern)
+ );
+ });
+
+ restart(handler);
+
+ return () -> close(handler);
}
+ private void restart(AbstractLifeCycle lifecycle) throws ServletException {
+ try {
+ lifecycle.start();
+ } catch (Exception e) {
+ if (e instanceof ServletException) {
+ throw (ServletException) e;
+ } else {
+ throw new ServletException("registerServlet() start failed", e);
+ }
+ }
+ }
+
+ private void close(ServletContextHandler handler) {
+ try {
+ handler.stop();
+ handler.destroy();
+ } catch (Exception e) {
+ LOG.error("close() failed", e);
+ }
+ contextHandlerCollection.removeHandler(handler);
+ }
}
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.ServletException;
-import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.aaa.web.FilterDetails;
import org.opendaylight.aaa.web.ServletDetails;
protected abstract WebServer getWebServer();
@Test
- @Ignore
public void testAddAfterStart() throws ServletException, IOException {
- WebContextBuilder webContextBuilder = WebContext.builder().contextPath("test1");
+ WebContextBuilder webContextBuilder = WebContext.builder().contextPath("/test1");
webContextBuilder.addServlet(
ServletDetails.builder().addUrlPattern("/*").name("Test").servlet(new TestServlet()).build());
WebContextRegistration webContextRegistration = getWebServer().registerWebContext(webContextBuilder.build());
}
@Test
- @Ignore
public void testAddFilter() throws Exception {
TestFilter testFilter = new TestFilter();
- WebContextBuilder webContextBuilder = WebContext.builder().contextPath("testingFilters");
+ WebContextBuilder webContextBuilder = WebContext.builder().contextPath("/testingFilters");
webContextBuilder
.putContextParam("testParam1", "avalue")
.addFilter(FilterDetails.builder().addUrlPattern("/*").name("Test").filter(testFilter).build());
}
@Test
- @Ignore
public void testRegisterListener() throws Exception {
- WebContextBuilder webContextBuilder = WebContext.builder().contextPath("testingListener");
+ WebContextBuilder webContextBuilder = WebContext.builder().contextPath("/testingListener");
TestListener testListener = new TestListener();
webContextBuilder.addListener(testListener);
assertThat(testListener.isInitialized).isFalse();
WebContextRegistration webContextRegistration = getWebServer().registerWebContext(webContextBuilder.build());
assertThat(testListener.isInitialized).isTrue();
webContextRegistration.close();
+ assertThat(testListener.isInitialized).isFalse();
}
static void checkTestServlet(String urlPrefix) throws IOException {