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.core;
20
21 import io.undertow.Handlers;
22 import io.undertow.predicate.Predicates;
23 import io.undertow.security.api.AuthenticationMechanism;
24 import io.undertow.security.api.AuthenticationMechanismFactory;
25 import io.undertow.security.api.NotificationReceiver;
26 import io.undertow.security.api.SecurityContextFactory;
27 import io.undertow.security.handlers.AuthenticationMechanismsHandler;
28 import io.undertow.security.handlers.NotificationReceiverHandler;
29 import io.undertow.security.handlers.SecurityInitialHandler;
30 import io.undertow.security.idm.IdentityManager;
31 import io.undertow.security.impl.BasicAuthenticationMechanism;
32 import io.undertow.security.impl.CachedAuthenticatedSessionMechanism;
33 import io.undertow.security.impl.ClientCertAuthenticationMechanism;
34 import io.undertow.security.impl.DigestAuthenticationMechanism;
35 import io.undertow.security.impl.ExternalAuthenticationMechanism;
36 import io.undertow.security.impl.GenericHeaderAuthenticationMechanism;
37 import io.undertow.security.impl.SecurityContextFactoryImpl;
38 import io.undertow.server.HandlerWrapper;
39 import io.undertow.server.HttpHandler;
40 import io.undertow.server.HttpServerExchange;
41 import io.undertow.server.handlers.HttpContinueReadHandler;
42 import io.undertow.server.handlers.PredicateHandler;
43 import io.undertow.server.handlers.form.FormEncodedDataDefinition;
44 import io.undertow.server.handlers.form.FormParserFactory;
45 import io.undertow.server.session.SessionListener;
46 import io.undertow.server.session.SessionManager;
47 import io.undertow.servlet.ServletExtension;
48 import io.undertow.servlet.UndertowServletLogger;
49 import io.undertow.servlet.UndertowServletMessages;
50 import io.undertow.servlet.api.AuthMethodConfig;
51 import io.undertow.servlet.api.Deployment;
52 import io.undertow.servlet.api.DeploymentInfo;
53 import io.undertow.servlet.api.DeploymentManager;
54 import io.undertow.servlet.api.ErrorPage;
55 import io.undertow.servlet.api.FilterInfo;
56 import io.undertow.servlet.api.HttpMethodSecurityInfo;
57 import io.undertow.servlet.api.InstanceHandle;
58 import io.undertow.servlet.api.ListenerInfo;
59 import io.undertow.servlet.api.LoginConfig;
60 import io.undertow.servlet.api.MetricsCollector;
61 import io.undertow.servlet.api.MimeMapping;
62 import io.undertow.servlet.api.SecurityConstraint;
63 import io.undertow.servlet.api.SecurityInfo.EmptyRoleSemantic;
64 import io.undertow.servlet.api.ServletContainer;
65 import io.undertow.servlet.api.ServletContainerInitializerInfo;
66 import io.undertow.servlet.api.ServletInfo;
67 import io.undertow.servlet.api.ServletSecurityInfo;
68 import io.undertow.servlet.api.ServletSessionConfig;
69 import io.undertow.servlet.api.ServletStackTraces;
70 import io.undertow.servlet.api.SessionPersistenceManager;
71 import io.undertow.servlet.api.ThreadSetupHandler;
72 import io.undertow.servlet.api.WebResourceCollection;
73 import io.undertow.servlet.handlers.CrawlerSessionManagerHandler;
74 import io.undertow.servlet.handlers.RedirectDirHandler;
75 import io.undertow.servlet.handlers.ServletDispatchingHandler;
76 import io.undertow.servlet.handlers.ServletHandler;
77 import io.undertow.servlet.handlers.ServletInitialHandler;
78 import io.undertow.servlet.handlers.SessionRestoringHandler;
79 import io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler;
80 import io.undertow.servlet.handlers.security.SSLInformationAssociationHandler;
81 import io.undertow.servlet.handlers.security.SecurityPathMatches;
82 import io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler;
83 import io.undertow.servlet.handlers.security.ServletAuthenticationConstraintHandler;
84 import io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler;
85 import io.undertow.servlet.handlers.security.ServletFormAuthenticationMechanism;
86 import io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler;
87 import io.undertow.servlet.predicate.DispatcherTypePredicate;
88 import io.undertow.servlet.spec.ServletContextImpl;
89 import io.undertow.servlet.spec.SessionCookieConfigImpl;
90 import io.undertow.util.MimeMappings;
91
92 import javax.servlet.ServletContainerInitializer;
93 import javax.servlet.ServletContext;
94 import javax.servlet.ServletContextEvent;
95 import javax.servlet.ServletContextListener;
96 import javax.servlet.ServletException;
97 import java.io.File;
98 import java.nio.charset.Charset;
99 import java.util.ArrayList;
100 import java.util.Collections;
101 import java.util.HashMap;
102 import java.util.HashSet;
103 import java.util.LinkedList;
104 import java.util.List;
105 import java.util.Locale;
106 import java.util.Map;
107 import java.util.ServiceLoader;
108 import java.util.Set;
109 import java.util.TreeMap;
110
111 import static javax.servlet.http.HttpServletRequest.BASIC_AUTH;
112 import static javax.servlet.http.HttpServletRequest.CLIENT_CERT_AUTH;
113 import static javax.servlet.http.HttpServletRequest.DIGEST_AUTH;
114 import static javax.servlet.http.HttpServletRequest.FORM_AUTH;
115
116 /**
117  * The deployment manager. This manager is responsible for controlling the lifecycle of a servlet deployment.
118  *
119  * @author Stuart Douglas
120  */

121 public class DeploymentManagerImpl implements DeploymentManager {
122
123     /**
124      * The original deployment information, this is
125      */

126     private final DeploymentInfo originalDeployment;
127
128     private final ServletContainer servletContainer;
129
130     /**
131      * Current deployment, this may be modified by SCI's
132      */

133     private volatile DeploymentImpl deployment;
134     private volatile State state = State.UNDEPLOYED;
135
136     public DeploymentManagerImpl(final DeploymentInfo deployment, final ServletContainer servletContainer) {
137         this.originalDeployment = deployment;
138         this.servletContainer = servletContainer;
139     }
140
141     @Override
142     public void deploy() {
143         final DeploymentInfo deploymentInfo = originalDeployment.clone();
144
145         if (deploymentInfo.getServletStackTraces() == ServletStackTraces.ALL) {
146             UndertowServletLogger.REQUEST_LOGGER.servletStackTracesAll(deploymentInfo.getDeploymentName());
147         }
148
149         deploymentInfo.validate();
150         final DeploymentImpl deployment = new DeploymentImpl(this, deploymentInfo, servletContainer);
151         this.deployment = deployment;
152
153         final ServletContextImpl servletContext = new ServletContextImpl(servletContainer, deployment);
154         deployment.setServletContext(servletContext);
155         handleExtensions(deploymentInfo, servletContext);
156
157         final List<ThreadSetupHandler> setup = new ArrayList<>();
158         setup.add(ServletRequestContextThreadSetupAction.INSTANCE);
159         setup.add(new ContextClassLoaderSetupAction(deploymentInfo.getClassLoader()));
160         setup.addAll(deploymentInfo.getThreadSetupActions());
161         deployment.setThreadSetupActions(setup);
162
163         deployment.getServletPaths().setWelcomePages(deploymentInfo.getWelcomePages());
164
165         if (deploymentInfo.getDefaultEncoding() != null) {
166             deployment.setDefaultCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
167         }
168         if(deploymentInfo.getDefaultRequestEncoding() != null) {
169             deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultRequestEncoding()));
170         } else if (deploymentInfo.getDefaultEncoding() != null) {
171             deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
172         }
173         if(deploymentInfo.getDefaultResponseEncoding() != null) {
174             deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultResponseEncoding()));
175         } else if (deploymentInfo.getDefaultEncoding() != null) {
176             deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultEncoding()));
177         }
178
179         handleDeploymentSessionConfig(deploymentInfo, servletContext);
180
181         deployment.setSessionManager(deploymentInfo.getSessionManagerFactory().createSessionManager(deployment));
182         deployment.getSessionManager().setDefaultSessionTimeout(deploymentInfo.getDefaultSessionTimeout());
183
184
185         try {
186             deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
187                 @Override
188                 public Void call(HttpServerExchange exchange, Object ignore) throws Exception {
189                     final ApplicationListeners listeners = createListeners();
190                     listeners.start();
191
192                     deployment.setApplicationListeners(listeners);
193
194                     //now create the servlets and filters that we know about. We can still get more later
195                     createServletsAndFilters(deployment, deploymentInfo);
196
197                     //first initialize the temp dir
198                     initializeTempDir(servletContext, deploymentInfo);
199
200                     //then run the SCI's
201                     for (final ServletContainerInitializerInfo sci : deploymentInfo.getServletContainerInitializers()) {
202                         final InstanceHandle<? extends ServletContainerInitializer> instance = sci.getInstanceFactory().createInstance();
203                         try {
204                             instance.getInstance().onStartup(sci.getHandlesTypes(), servletContext);
205                         } finally {
206                             instance.release();
207                         }
208                     }
209
210                     deployment.getSessionManager().registerSessionListener(new SessionListenerBridge(deployment, listeners, servletContext));
211                     for(SessionListener listener : deploymentInfo.getSessionListeners()) {
212                         deployment.getSessionManager().registerSessionListener(listener);
213                     }
214
215                     initializeErrorPages(deployment, deploymentInfo);
216                     initializeMimeMappings(deployment, deploymentInfo);
217                     listeners.contextInitialized();
218                     //run
219
220                     HttpHandler wrappedHandlers = ServletDispatchingHandler.INSTANCE;
221                     wrappedHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getInnerHandlerChainWrappers());
222                     wrappedHandlers = new RedirectDirHandler(wrappedHandlers, deployment.getServletPaths());
223                     if(!deploymentInfo.isSecurityDisabled()) {
224                         HttpHandler securityHandler = setupSecurityHandlers(wrappedHandlers);
225                         wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, securityHandler, wrappedHandlers);
226                     }
227                     HttpHandler outerHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getOuterHandlerChainWrappers());
228                     wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, outerHandlers, wrappedHandlers);
229                     wrappedHandlers = handleDevelopmentModePersistentSessions(wrappedHandlers, deploymentInfo, deployment.getSessionManager(), servletContext);
230
231                     MetricsCollector metrics = deploymentInfo.getMetricsCollector();
232                     if(metrics != null) {
233                         wrappedHandlers = new MetricsChainHandler(wrappedHandlers, metrics, deployment);
234                     }
235                     if( deploymentInfo.getCrawlerSessionManagerConfig() != null ) {
236                         wrappedHandlers = new CrawlerSessionManagerHandler(deploymentInfo.getCrawlerSessionManagerConfig(), wrappedHandlers);
237                     }
238
239                     final ServletInitialHandler servletInitialHandler = SecurityActions.createServletInitialHandler(deployment.getServletPaths(), wrappedHandlers, deployment, servletContext);
240
241                     HttpHandler initialHandler = wrapHandlers(servletInitialHandler, deployment.getDeploymentInfo().getInitialHandlerChainWrappers());
242                     initialHandler = new HttpContinueReadHandler(initialHandler);
243                     if(deploymentInfo.getUrlEncoding() != null) {
244                         initialHandler = Handlers.urlDecodingHandler(deploymentInfo.getUrlEncoding(), initialHandler);
245                     }
246                     deployment.setInitialHandler(initialHandler);
247                     deployment.setServletHandler(servletInitialHandler);
248                     deployment.getServletPaths().invalidate(); //make sure we have a fresh set of servlet paths
249                     servletContext.initDone();
250                     return null;
251                 }
252             }).call(nullnull);
253         } catch (Exception e) {
254             throw new RuntimeException(e);
255         }
256         //any problems with the paths won't get detected until the data is initialize
257         //so we force initialization here
258         deployment.getServletPaths().initData();
259         for(ServletContextListener listener : deploymentInfo.getDeploymentCompleteListeners()) {
260             listener.contextInitialized(new ServletContextEvent(servletContext));
261         }
262         state = State.DEPLOYED;
263     }
264
265     private void createServletsAndFilters(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) {
266         for (Map.Entry<String, ServletInfo> servlet : deploymentInfo.getServlets().entrySet()) {
267             deployment.getServlets().addServlet(servlet.getValue());
268         }
269         for (Map.Entry<String, FilterInfo> filter : deploymentInfo.getFilters().entrySet()) {
270             deployment.getFilters().addFilter(filter.getValue());
271         }
272     }
273
274     private void handleExtensions(final DeploymentInfo deploymentInfo, final ServletContextImpl servletContext) {
275         Set<String> loadedExtensions = new HashSet<>();
276
277         for (ServletExtension extension : ServiceLoader.load(ServletExtension.class, deploymentInfo.getClassLoader())) {
278             loadedExtensions.add(extension.getClass().getName());
279             extension.handleDeployment(deploymentInfo, servletContext);
280         }
281
282         if (ServletExtension.class.getClassLoader() != null && !ServletExtension.class.getClassLoader().equals(deploymentInfo.getClassLoader())) {
283             for (ServletExtension extension : ServiceLoader.load(ServletExtension.class)) {
284
285                 // Note: If the CLs are different, but can the see the same extensions and extension might get loaded
286                 // and thus instantiated twice, but the handleDeployment() is executed only once.
287
288                 if (!loadedExtensions.contains(extension.getClass().getName())) {
289                     extension.handleDeployment(deploymentInfo, servletContext);
290                 }
291             }
292         }
293
294         for (ServletExtension extension : ServletExtensionHolder.getServletExtensions()) {
295             if (!loadedExtensions.contains(extension.getClass().getName())) {
296                 extension.handleDeployment(deploymentInfo, servletContext);
297             }
298         }
299
300         for(ServletExtension extension : deploymentInfo.getServletExtensions()) {
301             extension.handleDeployment(deploymentInfo, servletContext);
302         }
303     }
304
305     /**
306      * sets up the outer security handlers.
307      * <p>
308      * the handler that actually performs the access check happens later in the chain, it is not setup here
309      *
310      * @param initialHandler The handler to wrap with security handlers
311      */

312     private HttpHandler setupSecurityHandlers(HttpHandler initialHandler) {
313         final DeploymentInfo deploymentInfo = deployment.getDeploymentInfo();
314         final LoginConfig loginConfig = deploymentInfo.getLoginConfig();
315
316         HttpHandler current = initialHandler;
317         current = new SSLInformationAssociationHandler(current);
318
319         final SecurityPathMatches securityPathMatches = buildSecurityConstraints();
320         securityPathMatches.logWarningsAboutUncoveredMethods();
321         current = new ServletAuthenticationCallHandler(current);
322
323         for(HandlerWrapper wrapper : deploymentInfo.getSecurityWrappers()) {
324             current = wrapper.wrap(current);
325         }
326
327         if(deploymentInfo.isDisableCachingForSecuredPages()) {
328             current = Handlers.predicate(Predicates.authRequired(), Handlers.disableCache(current), current);
329         }
330         if (!securityPathMatches.isEmpty()) {
331             current = new ServletAuthenticationConstraintHandler(current);
332         }
333         current = new ServletConfidentialityConstraintHandler(deploymentInfo.getConfidentialPortManager(), current);
334         if (!securityPathMatches.isEmpty()) {
335             current = new ServletSecurityConstraintHandler(securityPathMatches, current);
336         }
337
338         HandlerWrapper initialSecurityWrapper = deploymentInfo.getInitialSecurityWrapper();
339
340         String mechName = null;
341         if (initialSecurityWrapper == null) {
342             final Map<String, AuthenticationMechanismFactory> factoryMap = new HashMap<>(deploymentInfo.getAuthenticationMechanisms());
343             final IdentityManager identityManager = deploymentInfo.getIdentityManager();
344             if(!factoryMap.containsKey(BASIC_AUTH)) {
345                 factoryMap.put(BASIC_AUTH, BasicAuthenticationMechanism.FACTORY);
346             }
347             if(!factoryMap.containsKey(FORM_AUTH)) {
348                 factoryMap.put(FORM_AUTH, ServletFormAuthenticationMechanism.FACTORY);
349             }
350             if(!factoryMap.containsKey(DIGEST_AUTH)) {
351                 factoryMap.put(DIGEST_AUTH, DigestAuthenticationMechanism.FACTORY);
352             }
353             if(!factoryMap.containsKey(CLIENT_CERT_AUTH)) {
354                 factoryMap.put(CLIENT_CERT_AUTH, ClientCertAuthenticationMechanism.FACTORY);
355             }
356             if(!factoryMap.containsKey(ExternalAuthenticationMechanism.NAME)) {
357                 factoryMap.put(ExternalAuthenticationMechanism.NAME, ExternalAuthenticationMechanism.FACTORY);
358             }
359             if(!factoryMap.containsKey(GenericHeaderAuthenticationMechanism.NAME)) {
360                 factoryMap.put(GenericHeaderAuthenticationMechanism.NAME, GenericHeaderAuthenticationMechanism.FACTORY);
361             }
362             List<AuthenticationMechanism> authenticationMechanisms = new LinkedList<>();
363
364             if(deploymentInfo.isUseCachedAuthenticationMechanism()) {
365                 authenticationMechanisms.add(new CachedAuthenticatedSessionMechanism(identityManager));
366             }
367             if (loginConfig != null || deploymentInfo.getJaspiAuthenticationMechanism() != null) {
368
369                 //we don't allow multipart requests, and use the default encoding when it's set
370                 FormEncodedDataDefinition formEncodedDataDefinition = new FormEncodedDataDefinition();
371                 String reqEncoding = deploymentInfo.getDefaultRequestEncoding();
372                 if(reqEncoding == null) {
373                     reqEncoding = deploymentInfo.getDefaultEncoding();
374                 }
375                 if (reqEncoding != null) {
376                     formEncodedDataDefinition.setDefaultEncoding(reqEncoding);
377                 }
378                 FormParserFactory parser = FormParserFactory.builder(false)
379                         .addParser(formEncodedDataDefinition)
380                         .build();
381
382                 List<AuthMethodConfig> authMethods = Collections.<AuthMethodConfig>emptyList();
383                 if(loginConfig != null) {
384                     authMethods = loginConfig.getAuthMethods();
385                 }
386
387                 for(AuthMethodConfig method : authMethods) {
388                     AuthenticationMechanismFactory factory = factoryMap.get(method.getName());
389                     if(factory == null) {
390                         throw UndertowServletMessages.MESSAGES.unknownAuthenticationMechanism(method.getName());
391                     }
392                     if(mechName == null) {
393                         mechName = method.getName();
394                     }
395
396                     final Map<String, String> properties = new HashMap<>();
397                     properties.put(AuthenticationMechanismFactory.CONTEXT_PATH, deploymentInfo.getContextPath());
398                     properties.put(AuthenticationMechanismFactory.REALM, loginConfig.getRealmName());
399                     properties.put(AuthenticationMechanismFactory.ERROR_PAGE, loginConfig.getErrorPage());
400                     properties.put(AuthenticationMechanismFactory.LOGIN_PAGE, loginConfig.getLoginPage());
401                     properties.putAll(method.getProperties());
402
403                     String name = method.getName().toUpperCase(Locale.US);
404                     // The mechanism name is passed in from the HttpServletRequest interface as the name reported needs to be
405                     // comparable using '=='
406                     name = name.equals(FORM_AUTH) ? FORM_AUTH : name;
407                     name = name.equals(BASIC_AUTH) ? BASIC_AUTH : name;
408                     name = name.equals(DIGEST_AUTH) ? DIGEST_AUTH : name;
409                     name = name.equals(CLIENT_CERT_AUTH) ? CLIENT_CERT_AUTH : name;
410
411                     authenticationMechanisms.add(factory.create(name, identityManager, parser, properties));
412                 }
413             }
414
415
416             deployment.setAuthenticationMechanisms(authenticationMechanisms);
417             //if the JASPI auth mechanism is set then it takes over
418             if(deploymentInfo.getJaspiAuthenticationMechanism() == null) {
419                 current = new AuthenticationMechanismsHandler(current, authenticationMechanisms);
420             } else {
421                 current = new AuthenticationMechanismsHandler(current, Collections.<AuthenticationMechanism>singletonList(deploymentInfo.getJaspiAuthenticationMechanism()));
422             }
423
424             current = new CachedAuthenticatedSessionHandler(current, this.deployment.getServletContext());
425         }
426
427         List<NotificationReceiver> notificationReceivers = deploymentInfo.getNotificationReceivers();
428         if (!notificationReceivers.isEmpty()) {
429             current = new NotificationReceiverHandler(current, notificationReceivers);
430         }
431
432         if (initialSecurityWrapper == null) {
433             // TODO - A switch to constraint driven could be configurable, however before we can support that with servlets we would
434             // need additional tracking within sessions if a servlet has specifically requested that authentication occurs.
435             SecurityContextFactory contextFactory = deploymentInfo.getSecurityContextFactory();
436             if (contextFactory == null) {
437                 contextFactory = SecurityContextFactoryImpl.INSTANCE;
438             }
439             current = new SecurityInitialHandler(deploymentInfo.getAuthenticationMode(), deploymentInfo.getIdentityManager(), mechName,
440                     contextFactory, current);
441         } else {
442             current = initialSecurityWrapper.wrap(current);
443         }
444
445         return current;
446     }
447
448     private SecurityPathMatches buildSecurityConstraints() {
449         SecurityPathMatches.Builder builder = SecurityPathMatches.builder(deployment.getDeploymentInfo());
450         final Set<String> urlPatterns = new HashSet<>();
451         for (SecurityConstraint constraint : deployment.getDeploymentInfo().getSecurityConstraints()) {
452             builder.addSecurityConstraint(constraint);
453             for (WebResourceCollection webResources : constraint.getWebResourceCollections()) {
454                 urlPatterns.addAll(webResources.getUrlPatterns());
455             }
456         }
457
458         for (final ServletInfo servlet : deployment.getDeploymentInfo().getServlets().values()) {
459             final ServletSecurityInfo securityInfo = servlet.getServletSecurityInfo();
460             if (securityInfo != null) {
461                 final Set<String> mappings = new HashSet<>(servlet.getMappings());
462                 mappings.removeAll(urlPatterns);
463                 if (!mappings.isEmpty()) {
464                     final Set<String> methods = new HashSet<>();
465
466                     for (HttpMethodSecurityInfo method : securityInfo.getHttpMethodSecurityInfo()) {
467                         methods.add(method.getMethod());
468                         if (method.getRolesAllowed().isEmpty() && method.getEmptyRoleSemantic() == EmptyRoleSemantic.PERMIT) {
469                             //this is an implict allow
470                             continue;
471                         }
472                         SecurityConstraint newConstraint = new SecurityConstraint()
473                                 .addRolesAllowed(method.getRolesAllowed())
474                                 .setTransportGuaranteeType(method.getTransportGuaranteeType())
475                                 .addWebResourceCollection(new WebResourceCollection().addUrlPatterns(mappings)
476                                         .addHttpMethod(method.getMethod()));
477                         builder.addSecurityConstraint(newConstraint);
478                     }
479                     //now add the constraint, unless it has all default values and method constrains where specified
480                     if (!securityInfo.getRolesAllowed().isEmpty()
481                             || securityInfo.getEmptyRoleSemantic() != EmptyRoleSemantic.PERMIT
482                             || methods.isEmpty()) {
483                         SecurityConstraint newConstraint = new SecurityConstraint()
484                                 .setEmptyRoleSemantic(securityInfo.getEmptyRoleSemantic())
485                                 .addRolesAllowed(securityInfo.getRolesAllowed())
486                                 .setTransportGuaranteeType(securityInfo.getTransportGuaranteeType())
487                                 .addWebResourceCollection(new WebResourceCollection().addUrlPatterns(mappings)
488                                         .addHttpMethodOmissions(methods));
489                         builder.addSecurityConstraint(newConstraint);
490                     }
491                 }
492
493             }
494         }
495
496         return builder.build();
497     }
498
499     private void initializeTempDir(final ServletContextImpl servletContext, final DeploymentInfo deploymentInfo) {
500         if (deploymentInfo.getTempDir() != null) {
501             servletContext.setAttribute(ServletContext.TEMPDIR, deploymentInfo.getTempDir());
502         } else {
503             servletContext.setAttribute(ServletContext.TEMPDIR, new File(SecurityActions.getSystemProperty("java.io.tmpdir")));
504         }
505     }
506
507     private void initializeMimeMappings(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) {
508         final Map<String, String> mappings = new HashMap<>(MimeMappings.DEFAULT_MIME_MAPPINGS);
509         for (MimeMapping mapping : deploymentInfo.getMimeMappings()) {
510             mappings.put(mapping.getExtension().toLowerCase(Locale.ENGLISH), mapping.getMimeType());
511         }
512         deployment.setMimeExtensionMappings(mappings);
513     }
514
515     private void initializeErrorPages(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) {
516         final Map<Integer, String> codes = new HashMap<>();
517         final Map<Class<? extends Throwable>, String> exceptions = new HashMap<>();
518         String defaultErrorPage = null;
519         for (final ErrorPage page : deploymentInfo.getErrorPages()) {
520             if (page.getExceptionType() != null) {
521                 exceptions.put(page.getExceptionType(), page.getLocation());
522             } else if (page.getErrorCode() != null) {
523                 codes.put(page.getErrorCode(), page.getLocation());
524             } else {
525                 if (defaultErrorPage != null) {
526                     throw UndertowServletMessages.MESSAGES.moreThanOneDefaultErrorPage(defaultErrorPage, page.getLocation());
527                 } else {
528                     defaultErrorPage = page.getLocation();
529                 }
530             }
531         }
532         deployment.setErrorPages(new ErrorPages(codes, exceptions, defaultErrorPage));
533     }
534
535
536     private ApplicationListeners createListeners() {
537         final List<ManagedListener> managedListeners = new ArrayList<>();
538         for (final ListenerInfo listener : deployment.getDeploymentInfo().getListeners()) {
539             managedListeners.add(new ManagedListener(listener, listener.isProgramatic()));
540         }
541         return new ApplicationListeners(managedListeners, deployment.getServletContext());
542     }
543
544
545     private static HttpHandler wrapHandlers(final HttpHandler wrapee, final List<HandlerWrapper> wrappers) {
546         HttpHandler current = wrapee;
547         for (HandlerWrapper wrapper : wrappers) {
548             current = wrapper.wrap(current);
549         }
550         return current;
551     }
552
553     @Override
554     public HttpHandler start() throws ServletException {
555         try {
556             return deployment.createThreadSetupAction(new ThreadSetupHandler.Action<HttpHandler, Object>() {
557                 @Override
558                 public HttpHandler call(HttpServerExchange exchange, Object ignore) throws ServletException {
559                     deployment.getSessionManager().start();
560
561                     //we need to copy before iterating
562                     //because listeners can add other listeners
563                     ArrayList<Lifecycle> lifecycles = new ArrayList<>(deployment.getLifecycleObjects());
564                     for (Lifecycle object : lifecycles) {
565                         object.start();
566                     }
567                     HttpHandler root = deployment.getHandler();
568                     final TreeMap<Integer, List<ManagedServlet>> loadOnStartup = new TreeMap<>();
569                     for (Map.Entry<String, ServletHandler> entry : deployment.getServlets().getServletHandlers().entrySet()) {
570                         ManagedServlet servlet = entry.getValue().getManagedServlet();
571                         Integer loadOnStartupNumber = servlet.getServletInfo().getLoadOnStartup();
572                         if (loadOnStartupNumber != null) {
573                             if (loadOnStartupNumber < 0) {
574                                 continue;
575                             }
576                             List<ManagedServlet> list = loadOnStartup.get(loadOnStartupNumber);
577                             if (list == null) {
578                                 loadOnStartup.put(loadOnStartupNumber, list = new ArrayList<>());
579                             }
580                             list.add(servlet);
581                         }
582                     }
583                     for (Map.Entry<Integer, List<ManagedServlet>> load : loadOnStartup.entrySet()) {
584                         for (ManagedServlet servlet : load.getValue()) {
585                             servlet.createServlet();
586                         }
587                     }
588
589                     if (deployment.getDeploymentInfo().isEagerFilterInit()) {
590                         for (ManagedFilter filter : deployment.getFilters().getFilters().values()) {
591                             filter.createFilter();
592                         }
593                     }
594
595                     state = State.STARTED;
596                     return root;
597                 }
598             }).call(nullnull);
599         } catch (ServletException|RuntimeException e) {
600             throw e;
601         } catch (Exception e) {
602             throw new RuntimeException(e);
603         }
604     }
605
606     @Override
607     public void stop() throws ServletException {
608         try {
609             deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
610                 @Override
611                 public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
612                     for (Lifecycle object : deployment.getLifecycleObjects()) {
613                         try {
614                             object.stop();
615                         } catch (Throwable t) {
616                             UndertowServletLogger.ROOT_LOGGER.failedToDestroy(object, t);
617                         }
618                     }
619                     deployment.getSessionManager().stop();
620                     state = State.DEPLOYED;
621                     return null;
622                 }
623             }).call(nullnull);
624         } catch (ServletException|RuntimeException e) {
625             throw e;
626         } catch (Exception e) {
627             throw new RuntimeException(e);
628         }
629     }
630
631     private HttpHandler handleDevelopmentModePersistentSessions(HttpHandler next, final DeploymentInfo deploymentInfo, final SessionManager sessionManager, final ServletContextImpl servletContext) {
632         final SessionPersistenceManager sessionPersistenceManager = deploymentInfo.getSessionPersistenceManager();
633         if (sessionPersistenceManager != null) {
634             SessionRestoringHandler handler = new SessionRestoringHandler(deployment.getDeploymentInfo().getDeploymentName(), sessionManager, servletContext, next, sessionPersistenceManager);
635             deployment.addLifecycleObjects(handler);
636             return handler;
637         }
638         return next;
639     }
640
641     public void handleDeploymentSessionConfig(DeploymentInfo deploymentInfo, ServletContextImpl servletContext) {
642         SessionCookieConfigImpl sessionCookieConfig = servletContext.getSessionCookieConfig();
643         ServletSessionConfig sc = deploymentInfo.getServletSessionConfig();
644         if (sc != null) {
645             sessionCookieConfig.setName(sc.getName());
646             sessionCookieConfig.setComment(sc.getComment());
647             sessionCookieConfig.setDomain(sc.getDomain());
648             sessionCookieConfig.setHttpOnly(sc.isHttpOnly());
649             sessionCookieConfig.setMaxAge(sc.getMaxAge());
650             if(sc.getPath() != null) {
651                 sessionCookieConfig.setPath(sc.getPath());
652             } else {
653                 sessionCookieConfig.setPath(deploymentInfo.getContextPath());
654             }
655             sessionCookieConfig.setSecure(sc.isSecure());
656             if (sc.getSessionTrackingModes() != null) {
657                 servletContext.setDefaultSessionTrackingModes(new HashSet<>(sc.getSessionTrackingModes()));
658             }
659         }
660     }
661
662
663     @Override
664     public void undeploy() {
665         try {
666             deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() {
667                 @Override
668                 public Void call(HttpServerExchange exchange, Object ignore) throws ServletException {
669                     for(ServletContextListener listener : deployment.getDeploymentInfo().getDeploymentCompleteListeners()) {
670                         try {
671                             listener.contextDestroyed(new ServletContextEvent(deployment.getServletContext()));
672                         } catch (Throwable t) {
673                             UndertowServletLogger.REQUEST_LOGGER.failedToDestroy(listener, t);
674                         }
675                     }
676                     deployment.destroy();
677                     deployment = null;
678                     state = State.UNDEPLOYED;
679                     return null;
680                 }
681             }).call(nullnull);
682         } catch (Exception e) {
683             throw new RuntimeException(e);
684         }
685
686     }
687
688     @Override
689     public State getState() {
690         return state;
691     }
692
693     @Override
694     public Deployment getDeployment() {
695         return deployment;
696     }
697
698 }
699