Document and validate web-api constructs
[aaa.git] / web / api / src / main / java / org / opendaylight / aaa / web / ServletDetails.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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableMap;
14 import java.util.List;
15 import java.util.Map;
16 import javax.servlet.Servlet;
17 import org.eclipse.jdt.annotation.NonNull;
18
19 /**
20  * Details about a {@link Servlet}.
21  *
22  * @author Michael Vorburger.ch
23  */
24 public interface ServletDetails {
25     /**
26      * Get a {@link Servlet} instance.
27      *
28      * @return {@link Servlet} instance
29      */
30     @NonNull Servlet servlet();
31
32     /**
33      * Get Servlet's name.
34      *
35      * @return {@link String} servlet name
36      */
37     @NonNull String name();
38
39     /**
40      * Get list of servlet URL patterns. These patterns control how you access a servlet.
41      *
42      * <p>
43      * Restrictions to URLs and how it should look like are next:
44      * <ul>
45      *   <li>A string beginning with a ‘ / ’ character and ending with a ‘ /*’ suffix is used for path mapping.</li>
46      *   <li>A string beginning with a ‘ *. ’ prefix is used as an extension mapping.</li>
47      *   <li>The empty string ("") is a special URL pattern that exactly maps to the application's context root, i.e.,
48      *       requests of the form {@code http://host:port/context-root}. In this case the path info is ’ / ’ and the
49      *       servlet path and context path is empty string (““).</li>
50      *   <li>A string containing only the ’ / ’ character indicates the "default" servlet of the application. In this
51      *       case the servlet path is the request URI minus the context path and the path info is null.</li>
52      *   <li>All other strings are used for exact matches only.</li>
53      * </ul>
54      *
55      * @return {@link List} of Servlet URL patterns
56      * @see "Java Servlet Specification Version 3.1, Section 12.2 Specification of Mappings"
57      */
58     @NonNull List<String> urlPatterns();
59
60     /**
61      * Get Servlet initial parameters.
62      *
63      * @return {@link Map} that contains initial parameters
64      */
65     @NonNull Map<String, String> initParams();
66
67     /**
68      * Get indication whether {@link #servlet()} supports asynchronous processing.
69      *
70      * @return {@code true} if the filter supports asynchronous processing
71      * @see "Java Servlet Specification Version 3.1, Section 2.3.3.3 Asynchronous Processing"
72      */
73     boolean asyncSupported();
74
75     /**
76      * Create a builder for {@link ServletDetails}.
77      *
78      * @return {@link Builder} builder instance
79      */
80     static @NonNull Builder builder() {
81         return new Builder();
82     }
83
84     /**
85      * Builds instances of type {@link ServletDetails ServletDetails}. Initialize attributes and then invoke the
86      * {@link #build()} method to create an immutable instance.
87      *
88      * <p><em>{@code ServletDetails.Builder} is not thread-safe and generally should not be stored in a field or
89      * collection, but instead used immediately to create instances.</em>
90      */
91     final class Builder {
92         private record ImmutableServletDetails(Servlet servlet, String name, ImmutableList<String> urlPatterns,
93                 ImmutableMap<String, String> initParams, boolean asyncSupported) implements ServletDetails {
94             ImmutableServletDetails {
95                 if (urlPatterns.isEmpty()) {
96                     throw new IllegalStateException("No urlPattern specified");
97                 }
98             }
99         }
100
101         private final ImmutableMap.Builder<String, String> initParams = ImmutableMap.builder();
102         private final ImmutableList.Builder<String> urlPatterns = ImmutableList.builder();
103         private Servlet servlet;
104         private String name;
105         private boolean asyncSupported;
106
107         private Builder() {
108             // Hidden on purpose
109         }
110
111         /**
112          * Initializes the value for the {@link ServletDetails#servlet() servlet} attribute.
113          *
114          * @param servlet The value for servlet
115          * @return {@code this} builder for use in a chained invocation
116          */
117         @SuppressWarnings("checkstyle:hiddenField")
118         public @NonNull Builder servlet(final Servlet servlet) {
119             this.servlet = requireNonNull(servlet);
120             return this;
121         }
122
123         /**
124          * Initializes the value for the {@link ServletDetails#name() name} attribute.
125          *
126          * <p><em>If not set, this attribute will have a value corresponding to {@code servlet().getClass().getName()}.
127          * </em>
128          *
129          * @param name The value for name
130          * @return {@code this} builder for use in a chained invocation
131          * @throws NullPointerException if {code name} is {@code null}
132          */
133         @SuppressWarnings("checkstyle:hiddenField")
134         public @NonNull Builder name(final String name) {
135             this.name = requireNonNull(name);
136             return this;
137         }
138
139         /**
140          * Adds one element to {@link ServletDetails#urlPatterns() urlPatterns} list.
141          *
142          * @param urlPattern A urlPatterns element
143          * @return {@code this} builder for use in a chained invocation
144          * @throws NullPointerException if {code urlPattern} is {@code null}
145          * @throws IllegalArgumentException if {@code urlPattern} does not meet specification criteria
146          */
147         public @NonNull Builder addUrlPattern(final String urlPattern) {
148             urlPatterns.add(ServletSpec.requireMappingSpec(urlPattern));
149             return this;
150         }
151
152         /**
153          * Put one entry to the {@link ServletDetails#initParams() initParams} map.
154          *
155          * @param key The key in the initParams map
156          * @param value The associated value in the initParams map
157          * @return {@code this} builder for use in a chained invocation
158          * @throws NullPointerException if any argument is {@code null}
159          */
160         public @NonNull Builder putInitParam(final String key, final String value) {
161             initParams.put(key, value);
162             return this;
163         }
164
165         /**
166          * Initializes the value for the {@link ServletDetails#asyncSupported() asyncSupported} attribute.
167          *
168          * <p><em>If not set, this attribute will have a default value of {@code false}.</em>
169          *
170          * @param asyncSupported The value for asyncSupported
171          * @return {@code this} builder for use in a chained invocation
172          */
173         @SuppressWarnings("checkstyle:hiddenField")
174         public @NonNull Builder asyncSupported(final boolean asyncSupported) {
175             this.asyncSupported = asyncSupported;
176             return this;
177         }
178
179         /**
180          * Builds a new {@link ServletDetails ServletDetails}.
181          *
182          * @return An immutable instance of ServletDetails
183          * @throws IllegalStateException if any required attributes are missing
184          */
185         public @NonNull ServletDetails build() {
186             if (servlet == null) {
187                 throw new IllegalStateException("No servlet specified");
188             }
189             return new ImmutableServletDetails(servlet, name != null ? name : servlet.getClass().getName(),
190                 urlPatterns.build(), initParams.build(), asyncSupported);
191         }
192     }
193 }