a6022aec577eb6af149b7bca99271f109f329f56
[aaa.git] / web / api / src / main / java / org / opendaylight / aaa / web / WebContext.java
1 /*
2  * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.aaa.web;
9
10 import java.util.List;
11 import java.util.Map;
12 import javax.servlet.ServletContainerInitializer;
13 import javax.servlet.ServletContext;
14 import javax.servlet.ServletContextListener;
15 import javax.servlet.ServletRegistration;
16 import org.immutables.value.Value;
17 import org.immutables.value.Value.Default;
18
19 /**
20  * Web Context with URL prefix. AKA Web App or Servlet context.
21  *
22  * <p>
23  * Its {@link WebContextBuilder} allows programmatic web component registration
24  * (as opposed to declarative e.g. via web.xml, OSGi HTTP Whiteboard blueprint
25  * integration, CXF BP etc.)
26  *
27  * <p>
28  * This is preferable because:
29  * <ul>
30  * <li>using code instead of hiding class names in XML enables tools such as
31  * e.g. BND (in the maven-bundle-plugin) to correctly figure dependencies e.g.
32  * for OSGi Import-Package headers;
33  *
34  * <li>explicit passing of web components instances, instead of providing class
35  * names in XML files and letting a web container create the new instances using
36  * the default constructor, solves a pesky dependency injection (DI) related
37  * problem which typically leads to weird hoops in code through
38  * <code>static</code> etc. that can be avoided using this;
39  *
40  * <li>tests can more easily programmatically instantiate web components.
41  * </ul>
42  *
43  * <p>
44  * This, not surprisingly, looks somewhat like a Servlet (3.x)
45  * {@link ServletContext}, which also allows programmatic dynamic registration
46  * e.g. via {@link ServletRegistration}; however in practice direct use of that
47  * API has been found to be problematic under OSGi, because it is intended for
48  * JSE and <a href="https://github.com/eclipse/jetty.project/issues/1395">does
49  * not easily appear to permit dynamic registration at any time</a> (only during
50  * Servlet container initialization time by
51  * {@link ServletContainerInitializer}), and is generally less clear to use than
52  * this simple API which intentionally maps directly to what one would have
53  * declared in a web.xml file. This API is also slightly more focused and drops
54  * a number of concepts that API has which we do not want to support here
55  * (including e.g. security, roles, multipart etc.)
56  *
57  * <p>
58  * It also looks somewhat similar to the OSGi HttpService, but we want to avoid
59  * any org.osgi dependency (both API and impl) here, and that API is also less
60  * clear (and uses an ancient (!) {@link java.util.Dictionary} in its method
61  * signature), and -most importantly- simply does not support Filters and Listeners, only
62  * Servlets. The Pax Web API does extend the base OSGi API and adds supports for
63  * Filters, Listeners and context parameters, but is still OSGi specific,
64  * whereas this offers a much simpler standalone API without OSGi dependency.
65  * (The Pax Web API also has confusing signatures in its registerFilter() methods,
66  * where one can easily confuse which String[] is the urlPatterns;
67  * which we had initially done accidentally; and left AAA broken.)
68  *
69  * <p>
70  * This is immutable, with a Builder, because contrary to a declarative approach
71  * in a file such as web.xml, the registration order very much matters (e.g. an
72  * context parameter added after a Servlet registration would not be seen by that
73  * Servlet; or a Filter added to protect a Servlet might not yet be active
74  * for an instant if the registerServlet is before the registerFilter).
75  * Therefore, this API enforces atomicity and lets clients first register
76  * everything on the Builder, and only then use
77  * {@link WebServer#registerWebContext(WebContext)}.
78  *
79  * @author Michael Vorburger.ch
80  */
81 @Value.Immutable
82 @Value.Style(visibility = Value.Style.ImplementationVisibility.PRIVATE, depluralize = true)
83 public abstract class WebContext {
84
85     public static WebContextBuilder builder() {
86         return new WebContextBuilder();
87     }
88
89     /**
90      * Path which will be used as URL prefix to all registered servlets and filters.
91      */
92     public abstract String contextPath();
93
94     /**
95      * Flag whether this context supports web sessions, defaults to true.
96      */
97     @Default
98     public boolean supportsSessions() {
99         return true;
100     }
101
102     /**
103      * Servlets.
104      */
105     public abstract List<ServletDetails> servlets();
106
107     /**
108      * Filters.
109      */
110     public abstract List<FilterDetails> filters();
111
112     /**
113      * Listeners.
114      */
115     public abstract List<ServletContextListener> listeners();
116
117     /**
118      * Registers resources (eg html files) that can be accessed via the URI namespace.
119      */
120     public abstract List<ResourceDetails> resources();
121
122     /**
123      * Context params. NB: These are the web context's wide parameters; contrary to
124      * individual {@link ServletDetails#initParams()} and
125      * {@link FilterDetails#initParams()}.
126      */
127     public abstract Map<String, String> contextParams();
128
129     @Value.Check
130     protected void check() {
131         servlets().forEach(servlet -> {
132             if (servlet.urlPatterns().isEmpty()) {
133                 throw new IllegalArgumentException("Servlet has no URL: " + servlet.name());
134             }
135         });
136         filters().forEach(filter -> {
137             if (filter.urlPatterns().isEmpty()) {
138                 throw new IllegalArgumentException("Filter has no URL: " + filter.name());
139             }
140         });
141     }
142 }