1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.servlet.api;
20
21 import java.lang.reflect.Constructor;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.Executor;
29
30 import javax.servlet.MultipartConfigElement;
31 import javax.servlet.Servlet;
32
33 import io.undertow.server.HandlerWrapper;
34 import io.undertow.servlet.UndertowServletMessages;
35 import io.undertow.servlet.util.ConstructorInstanceFactory;
36
37 /**
38  * @author Stuart Douglas
39  */

40 public class ServletInfo implements Cloneable {
41
42     private final Class<? extends Servlet> servletClass;
43     private final String name;
44
45     private final List<String> mappings = new ArrayList<>();
46     private final Map<String, String> initParams = new HashMap<>();
47     private final List<SecurityRoleRef> securityRoleRefs = new ArrayList<>();
48     private final List<HandlerWrapper> handlerChainWrappers = new ArrayList<>();
49
50     private InstanceFactory<? extends Servlet> instanceFactory;
51     private String jspFile;
52     private Integer loadOnStartup;
53     private boolean enabled;
54     private boolean asyncSupported;
55     private String runAs;
56     private MultipartConfigElement multipartConfig;
57     private ServletSecurityInfo servletSecurityInfo;
58     private Executor executor;
59     /**
60      * If this is true this servlet will not be considered when evaluating welcome file mappings,
61      * and if the mapped path is a directory a welcome file match will be performed that may result in another servlet
62      * being selected.
63      *
64      * Generally intended to be used by the default and JSP servlet.
65      */

66     private boolean requireWelcomeFileMapping;
67
68     public ServletInfo(final String name, final Class<? extends Servlet> servletClass) {
69         if (name == null) {
70             throw UndertowServletMessages.MESSAGES.paramCannotBeNull("name");
71         }
72         if (servletClass == null) {
73             throw UndertowServletMessages.MESSAGES.paramCannotBeNull("servletClass""Servlet", name);
74         }
75         if (!Servlet.class.isAssignableFrom(servletClass)) {
76             throw UndertowServletMessages.MESSAGES.servletMustImplementServlet(name, servletClass);
77         }
78         try {
79             final Constructor<? extends Servlet> ctor = servletClass.getDeclaredConstructor();
80             ctor.setAccessible(true);
81             this.instanceFactory = new ConstructorInstanceFactory(ctor);
82             this.name = name;
83             this.servletClass = servletClass;
84         } catch (NoSuchMethodException e) {
85             throw UndertowServletMessages.MESSAGES.componentMustHaveDefaultConstructor("Servlet", servletClass);
86         }
87     }
88
89
90     public ServletInfo(final String name, final Class<? extends Servlet> servletClass, final InstanceFactory<? extends Servlet> instanceFactory) {
91         if (name == null) {
92             throw UndertowServletMessages.MESSAGES.paramCannotBeNull("name");
93         }
94         if (servletClass == null) {
95             throw UndertowServletMessages.MESSAGES.paramCannotBeNull("servletClass""Servlet", name);
96         }
97         if (!Servlet.class.isAssignableFrom(servletClass)) {
98             throw UndertowServletMessages.MESSAGES.servletMustImplementServlet(name, servletClass);
99         }
100         this.instanceFactory = instanceFactory;
101         this.name = name;
102         this.servletClass = servletClass;
103     }
104
105     public void validate() {
106         //TODO
107     }
108
109     @Override
110     public ServletInfo clone() {
111         ServletInfo info = new ServletInfo(name, servletClass, instanceFactory)
112                 .setJspFile(jspFile)
113                 .setLoadOnStartup(loadOnStartup)
114                 .setEnabled(enabled)
115                 .setAsyncSupported(asyncSupported)
116                 .setRunAs(runAs)
117                 .setMultipartConfig(multipartConfig)
118                 .setExecutor(executor)
119                 .setRequireWelcomeFileMapping(requireWelcomeFileMapping);
120         info.mappings.addAll(mappings);
121         info.initParams.putAll(initParams);
122         info.securityRoleRefs.addAll(securityRoleRefs);
123         info.handlerChainWrappers.addAll(handlerChainWrappers);
124         if (servletSecurityInfo != null) {
125             info.servletSecurityInfo = servletSecurityInfo.clone();
126         }
127         return info;
128     }
129
130     public Class<? extends Servlet> getServletClass() {
131         return servletClass;
132     }
133
134     public String getName() {
135         return name;
136     }
137
138     public void setInstanceFactory(final InstanceFactory<? extends Servlet> instanceFactory) {
139         if (instanceFactory == null) {
140             throw UndertowServletMessages.MESSAGES.paramCannotBeNull("instanceFactory");
141         }
142         this.instanceFactory = instanceFactory;
143     }
144
145     public InstanceFactory<? extends Servlet> getInstanceFactory() {
146         return instanceFactory;
147     }
148
149     public List<String> getMappings() {
150         return Collections.unmodifiableList(mappings);
151     }
152
153     public ServletInfo addMapping(final String mapping) {
154         if(!mapping.startsWith("/") && !mapping.startsWith("*") && !mapping.isEmpty()) {
155             //if the user adds a mapping like 'index.html' we transparently translate it to '/index.html'
156             mappings.add("/" + mapping);
157         } else {
158             mappings.add(mapping);
159         }
160         return this;
161     }
162
163
164     public ServletInfo addMappings(final Collection<String> mappings) {
165         for(String m : mappings) {
166             addMapping(m);
167         }
168         return this;
169     }
170
171
172     public ServletInfo addMappings(final String... mappings) {
173         for(String m : mappings) {
174             addMapping(m);
175         }
176         return this;
177     }
178
179     public ServletInfo addInitParam(final String name, final String value) {
180         initParams.put(name, value);
181         return this;
182     }
183
184
185     public Map<String, String> getInitParams() {
186         return Collections.unmodifiableMap(initParams);
187     }
188
189     public String getJspFile() {
190         return jspFile;
191     }
192
193     public ServletInfo setJspFile(final String jspFile) {
194         this.jspFile = jspFile;
195         return this;
196     }
197
198     public Integer getLoadOnStartup() {
199         return loadOnStartup;
200     }
201
202     public ServletInfo setLoadOnStartup(final Integer loadOnStartup) {
203         this.loadOnStartup = loadOnStartup;
204         return this;
205     }
206
207     public boolean isAsyncSupported() {
208         return asyncSupported;
209     }
210
211     public ServletInfo setAsyncSupported(final boolean asyncSupported) {
212         this.asyncSupported = asyncSupported;
213         return this;
214     }
215
216     public boolean isEnabled() {
217         return enabled;
218     }
219
220     public ServletInfo setEnabled(final boolean enabled) {
221         this.enabled = enabled;
222         return this;
223     }
224
225     public String getRunAs() {
226         return runAs;
227     }
228
229     public ServletInfo setRunAs(final String runAs) {
230         this.runAs = runAs;
231         return this;
232     }
233
234     public MultipartConfigElement getMultipartConfig() {
235         return multipartConfig;
236     }
237
238     public ServletInfo setMultipartConfig(final MultipartConfigElement multipartConfig) {
239         this.multipartConfig = multipartConfig;
240         return this;
241     }
242
243     public void addSecurityRoleRef(final String role, final String linkedRole) {
244         this.securityRoleRefs.add(new SecurityRoleRef(role, linkedRole));
245     }
246
247     public List<SecurityRoleRef> getSecurityRoleRefs() {
248         return Collections.unmodifiableList(securityRoleRefs);
249     }
250
251     public ServletInfo addHandlerChainWrapper(final HandlerWrapper wrapper) {
252         this.handlerChainWrappers.add(wrapper);
253         return this;
254     }
255
256     public List<HandlerWrapper> getHandlerChainWrappers() {
257         return Collections.unmodifiableList(handlerChainWrappers);
258     }
259
260     public ServletSecurityInfo getServletSecurityInfo() {
261         return servletSecurityInfo;
262     }
263
264     public ServletInfo setServletSecurityInfo(final ServletSecurityInfo servletSecurityInfo) {
265         this.servletSecurityInfo = servletSecurityInfo;
266         return this;
267     }
268
269     public Executor getExecutor() {
270         return executor;
271     }
272
273     public ServletInfo setExecutor(final Executor executor) {
274         this.executor = executor;
275         return this;
276     }
277
278     /**
279      *
280      * @return
281      */

282     public boolean isRequireWelcomeFileMapping() {
283         return requireWelcomeFileMapping;
284     }
285
286     public ServletInfo setRequireWelcomeFileMapping(boolean requireWelcomeFileMapping) {
287         this.requireWelcomeFileMapping = requireWelcomeFileMapping;
288         return this;
289     }
290
291     @Override
292     public String toString() {
293         return "ServletInfo{" +
294                 "mappings=" + mappings +
295                 ", servletClass=" + servletClass +
296                 ", name='" + name + '\'' +
297                 '}';
298     }
299 }
300