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.osgi;
10 import java.util.ArrayList;
11 import java.util.EventListener;
12 import java.util.List;
14 import javax.inject.Inject;
15 import javax.servlet.Filter;
16 import javax.servlet.Servlet;
17 import javax.servlet.ServletContextListener;
18 import javax.servlet.ServletException;
19 import org.opendaylight.aaa.web.ServletDetails;
20 import org.opendaylight.aaa.web.WebContext;
21 import org.opendaylight.aaa.web.WebContextRegistration;
22 import org.opendaylight.aaa.web.WebServer;
23 import org.ops4j.pax.cdi.api.OsgiService;
24 import org.ops4j.pax.web.service.WebContainer;
25 import org.ops4j.pax.web.service.WebContainerDTO;
26 import org.osgi.service.http.HttpContext;
27 import org.osgi.service.http.HttpService;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * {@link WebServer} (and {@link WebContext}) bridge implementation
33 * delegating to Pax Web WebContainer (which extends an OSGi {@link HttpService}).
35 * @author Michael Vorburger.ch
37 // This is a utility class and cannot be a @Singleton @OsgiServiceProvider
38 // (because Pax Web handles class loading relative to its calling bundle)
39 public class PaxWebServer implements WebServer {
41 // TODO write an IT (using Pax Exam) which tests this, re-use JettyLauncherTest
43 private static final Logger LOG = LoggerFactory.getLogger(PaxWebServer.class);
45 private final WebContainer paxWeb;
48 public PaxWebServer(@OsgiService WebContainer paxWebContainer) {
49 this.paxWeb = paxWebContainer;
53 public String getBaseURL() {
54 WebContainerDTO details = paxWeb.getWebcontainerDTO();
55 return "http://" + details.listeningAddresses[0] + ":" + details.port;
59 public WebContextRegistration registerWebContext(WebContext webContext) throws ServletException {
60 return new WebContextImpl(webContext);
63 private class WebContextImpl implements WebContextRegistration {
65 private final String contextPath;
67 private final List<Servlet> registeredServlets = new ArrayList<>();
68 private final List<EventListener> registeredEventListeners = new ArrayList<>();
69 private final List<Filter> registeredFilters = new ArrayList<>();
71 WebContextImpl(WebContext webContext) throws ServletException {
72 // We ignore webContext.supportsSessions() because the OSGi HttpService / Pax Web API
73 // does not seem to support not wanting session support on some web contexts
74 // (it assumes always with session); but other implementation support without.
76 this.contextPath = webContext.contextPath();
78 // NB This is NOT the URL prefix of the context, but the context.id which is
79 // used while registering the HttpContext in the OSGi service registry.
80 String contextID = contextPath + ".id";
82 HttpContext osgiHttpContext = paxWeb.createDefaultHttpContext(contextID);
83 paxWeb.begin(osgiHttpContext);
85 // The order in which we set things up here matters...
87 // 1. Context parameters - because listeners, filters and servlets could need them
88 paxWeb.setContextParam(new MapDictionary<>(webContext.contextParams()), osgiHttpContext);
90 // 2. Listeners - because they could set up things that filters and servlets need
91 webContext.listeners().forEach(listener -> registerListener(osgiHttpContext, listener));
93 // 3. Filters - because subsequent servlets should already be covered by the filters
94 webContext.filters().forEach(filter ->
95 registerFilter(osgiHttpContext, filter.urlPatterns(), filter.name(), filter.filter(),
96 filter.initParams()));
98 // 4. servlets - 'bout time for 'em by now, don't you think? ;)
99 for (ServletDetails servlet : webContext.servlets()) {
100 registerServlet(osgiHttpContext, servlet.urlPatterns(), servlet.name(), servlet.servlet(),
101 servlet.initParams());
104 paxWeb.end(osgiHttpContext);
107 void registerFilter(HttpContext osgiHttpContext, List<String> urlPatterns, String name, Filter filter,
108 Map<String, String> params) {
109 boolean asyncSupported = false;
110 String[] absUrlPatterns = absolute(urlPatterns);
111 LOG.info("Registering Filter for aliases {}: {}", absUrlPatterns, filter);
112 paxWeb.registerFilter(filter, absUrlPatterns, new String[] { name }, new MapDictionary<>(params),
113 asyncSupported, osgiHttpContext);
114 registeredFilters.add(filter);
117 String[] absolute(List<String> relatives) {
118 return relatives.stream().map(urlPattern -> contextPath + urlPattern).toArray(String[]::new);
121 void registerServlet(HttpContext osgiHttpContext, List<String> urlPatterns, String name, Servlet servlet,
122 Map<String, String> params) throws ServletException {
123 int loadOnStartup = 1;
124 boolean asyncSupported = false;
125 String[] absUrlPatterns = absolute(urlPatterns);
126 LOG.info("Registering Servlet for aliases {}: {}", absUrlPatterns, servlet);
127 paxWeb.registerServlet(servlet, name, absUrlPatterns, new MapDictionary<>(params), loadOnStartup,
128 asyncSupported, osgiHttpContext);
129 registeredServlets.add(servlet);
132 void registerListener(HttpContext osgiHttpContext, ServletContextListener listener) {
133 paxWeb.registerEventListener(listener, osgiHttpContext);
134 registeredEventListeners.add(listener);
138 public void close() {
139 // The order is relevant here.. Servlets first, then Filters, Listeners last; this is the inverse of above
140 for (Servlet registeredServlet : registeredServlets) {
141 paxWeb.unregisterServlet(registeredServlet);
143 for (Filter filter : registeredFilters) {
144 paxWeb.unregisterFilter(filter);
146 for (EventListener eventListener : registeredEventListeners) {
147 paxWeb.unregisterEventListener(eventListener);