2 * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.aaa.web.jetty;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import java.util.EnumSet;
13 import javax.annotation.PostConstruct;
14 import javax.annotation.PreDestroy;
15 import javax.inject.Singleton;
16 import javax.servlet.DispatcherType;
17 import javax.servlet.ServletException;
18 import org.eclipse.jetty.server.Server;
19 import org.eclipse.jetty.server.ServerConnector;
20 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
21 import org.eclipse.jetty.servlet.FilterHolder;
22 import org.eclipse.jetty.servlet.ServletContextHandler;
23 import org.eclipse.jetty.servlet.ServletHolder;
24 import org.eclipse.jetty.util.component.AbstractLifeCycle;
25 import org.opendaylight.aaa.web.WebContext;
26 import org.opendaylight.aaa.web.WebServer;
27 import org.opendaylight.yangtools.concepts.Registration;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * {@link WebServer} (and {@link WebContext}) implementation based on Jetty.
34 * @author Michael Vorburger.ch
37 public class JettyWebServer implements WebServer {
39 private static final Logger LOG = LoggerFactory.getLogger(JettyWebServer.class);
41 private static final int HTTP_SERVER_IDLE_TIMEOUT = 30000;
44 private final Server server;
45 private final ServerConnector http;
46 private final ContextHandlerCollection contextHandlerCollection;
48 public JettyWebServer() {
49 // automatically choose free port
53 public JettyWebServer(final int httpPort) {
54 checkArgument(httpPort >= 0, "httpPort must be positive");
55 checkArgument(httpPort < 65536, "httpPort must < 65536");
57 server = new Server();
58 server.setStopAtShutdown(true);
60 http = new ServerConnector(server);
61 http.setHost("localhost");
62 http.setPort(httpPort);
63 http.setIdleTimeout(HTTP_SERVER_IDLE_TIMEOUT);
64 server.addConnector(http);
66 contextHandlerCollection = new ContextHandlerCollection();
67 server.setHandler(contextHandlerCollection);
71 public String getBaseURL() {
73 throw new IllegalStateException("must start() before getBaseURL()");
75 return "http://localhost:" + httpPort;
79 public void start() throws Exception {
81 httpPort = http.getLocalPort();
82 LOG.info("Started Jetty-based HTTP web server on port {} ({}).", httpPort, hashCode());
86 public void stop() throws Exception {
87 LOG.info("Stopping Jetty-based web server...");
88 // NB server.stop() will call stop() on all ServletContextHandler/WebAppContext
90 LOG.info("Stopped Jetty-based web server.");
94 public synchronized Registration registerWebContext(final WebContext webContext) throws ServletException {
95 String contextPathWithSlashPrefix = webContext.contextPath().startsWith("/")
96 ? webContext.contextPath() : "/" + webContext.contextPath();
97 ServletContextHandler handler = new ServletContextHandler(contextHandlerCollection, contextPathWithSlashPrefix,
98 webContext.supportsSessions() ? ServletContextHandler.SESSIONS : ServletContextHandler.NO_SESSIONS);
100 // The order in which we do things here must be the same as
101 // the equivalent in org.opendaylight.aaa.web.osgi.PaxWebServer
103 // 1. Context parameters - because listeners, filters and servlets could need them
104 webContext.contextParams().entrySet().forEach(entry -> handler.setAttribute(entry.getKey(), entry.getValue()));
105 // also handler.getServletContext().setAttribute(name, value), both seem work
107 // 2. Listeners - because they could set up things that filters and servlets need
108 webContext.listeners().forEach(listener -> handler.addEventListener(listener));
110 // 3. Filters - because subsequent servlets should already be covered by the filters
111 webContext.filters().forEach(filter -> {
112 FilterHolder filterHolder = new FilterHolder(filter.filter());
113 filterHolder.setInitParameters(filter.initParams());
114 filter.urlPatterns().forEach(
115 urlPattern -> handler.addFilter(filterHolder, urlPattern, EnumSet.allOf(DispatcherType.class))
119 // 4. servlets - 'bout time for 'em by now, don't you think? ;)
120 webContext.servlets().forEach(servlet -> {
121 ServletHolder servletHolder = new ServletHolder(servlet.name(), servlet.servlet());
122 servletHolder.setInitParameters(servlet.initParams());
123 servletHolder.setAsyncSupported(servlet.getAsyncSupported());
124 // AKA <load-on-startup> 1
125 servletHolder.setInitOrder(1);
126 servlet.urlPatterns().forEach(
127 urlPattern -> handler.addServlet(servletHolder, urlPattern)
133 return () -> close(handler);
136 @SuppressWarnings("checkstyle:IllegalCatch")
137 private static void restart(final AbstractLifeCycle lifecycle) throws ServletException {
140 } catch (ServletException | RuntimeException e) {
142 } catch (Exception e) {
143 throw new ServletException("registerServlet() start failed", e);
147 @SuppressWarnings("checkstyle:IllegalCatch")
148 private void close(final ServletContextHandler handler) {
151 } catch (Exception e) {
152 LOG.error("close() failed", e);
156 contextHandlerCollection.removeHandler(handler);