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

package io.undertow.servlet.core;

import io.undertow.Handlers;
import io.undertow.predicate.Predicates;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.AuthenticationMechanismFactory;
import io.undertow.security.api.NotificationReceiver;
import io.undertow.security.api.SecurityContextFactory;
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
import io.undertow.security.handlers.NotificationReceiverHandler;
import io.undertow.security.handlers.SecurityInitialHandler;
import io.undertow.security.idm.IdentityManager;
import io.undertow.security.impl.BasicAuthenticationMechanism;
import io.undertow.security.impl.CachedAuthenticatedSessionMechanism;
import io.undertow.security.impl.ClientCertAuthenticationMechanism;
import io.undertow.security.impl.DigestAuthenticationMechanism;
import io.undertow.security.impl.ExternalAuthenticationMechanism;
import io.undertow.security.impl.GenericHeaderAuthenticationMechanism;
import io.undertow.security.impl.SecurityContextFactoryImpl;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.HttpContinueReadHandler;
import io.undertow.server.handlers.PredicateHandler;
import io.undertow.server.handlers.form.FormEncodedDataDefinition;
import io.undertow.server.handlers.form.FormParserFactory;
import io.undertow.server.session.SessionListener;
import io.undertow.server.session.SessionManager;
import io.undertow.servlet.ServletExtension;
import io.undertow.servlet.UndertowServletLogger;
import io.undertow.servlet.UndertowServletMessages;
import io.undertow.servlet.api.AuthMethodConfig;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.ErrorPage;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.HttpMethodSecurityInfo;
import io.undertow.servlet.api.InstanceHandle;
import io.undertow.servlet.api.ListenerInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.MetricsCollector;
import io.undertow.servlet.api.MimeMapping;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.SecurityInfo.EmptyRoleSemantic;
import io.undertow.servlet.api.ServletContainer;
import io.undertow.servlet.api.ServletContainerInitializerInfo;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.ServletSecurityInfo;
import io.undertow.servlet.api.ServletSessionConfig;
import io.undertow.servlet.api.ServletStackTraces;
import io.undertow.servlet.api.SessionPersistenceManager;
import io.undertow.servlet.api.ThreadSetupHandler;
import io.undertow.servlet.api.WebResourceCollection;
import io.undertow.servlet.handlers.CrawlerSessionManagerHandler;
import io.undertow.servlet.handlers.RedirectDirHandler;
import io.undertow.servlet.handlers.SendErrorPageHandler;
import io.undertow.servlet.handlers.ServletDispatchingHandler;
import io.undertow.servlet.handlers.ServletHandler;
import io.undertow.servlet.handlers.ServletInitialHandler;
import io.undertow.servlet.handlers.SessionRestoringHandler;
import io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler;
import io.undertow.servlet.handlers.security.SSLInformationAssociationHandler;
import io.undertow.servlet.handlers.security.SecurityPathMatches;
import io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler;
import io.undertow.servlet.handlers.security.ServletAuthenticationConstraintHandler;
import io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler;
import io.undertow.servlet.handlers.security.ServletFormAuthenticationMechanism;
import io.undertow.servlet.handlers.security.ServletSecurityConstraintHandler;
import io.undertow.servlet.predicate.DispatcherTypePredicate;
import io.undertow.servlet.spec.ServletContextImpl;
import io.undertow.servlet.spec.SessionCookieConfigImpl;
import io.undertow.util.MimeMappings;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;

import static javax.servlet.http.HttpServletRequest.BASIC_AUTH;
import static javax.servlet.http.HttpServletRequest.CLIENT_CERT_AUTH;
import static javax.servlet.http.HttpServletRequest.DIGEST_AUTH;
import static javax.servlet.http.HttpServletRequest.FORM_AUTH;

The deployment manager. This manager is responsible for controlling the lifecycle of a servlet deployment.
Author:Stuart Douglas
/** * The deployment manager. This manager is responsible for controlling the lifecycle of a servlet deployment. * * @author Stuart Douglas */
public class DeploymentManagerImpl implements DeploymentManager {
The original deployment information, this is
/** * The original deployment information, this is */
private final DeploymentInfo originalDeployment; private final ServletContainer servletContainer;
Current deployment, this may be modified by SCI's
/** * Current deployment, this may be modified by SCI's */
private volatile DeploymentImpl deployment; private volatile State state = State.UNDEPLOYED; public DeploymentManagerImpl(final DeploymentInfo deployment, final ServletContainer servletContainer) { this.originalDeployment = deployment; this.servletContainer = servletContainer; } @Override public void deploy() { final DeploymentInfo deploymentInfo = originalDeployment.clone(); if (deploymentInfo.getServletStackTraces() == ServletStackTraces.ALL) { UndertowServletLogger.REQUEST_LOGGER.servletStackTracesAll(deploymentInfo.getDeploymentName()); } deploymentInfo.validate(); final DeploymentImpl deployment = new DeploymentImpl(this, deploymentInfo, servletContainer); this.deployment = deployment; final ServletContextImpl servletContext = new ServletContextImpl(servletContainer, deployment); deployment.setServletContext(servletContext); handleExtensions(deploymentInfo, servletContext); final List<ThreadSetupHandler> setup = new ArrayList<>(); setup.add(ServletRequestContextThreadSetupAction.INSTANCE); setup.add(new ContextClassLoaderSetupAction(deploymentInfo.getClassLoader())); setup.addAll(deploymentInfo.getThreadSetupActions()); deployment.setThreadSetupActions(setup); deployment.getServletPaths().setWelcomePages(deploymentInfo.getWelcomePages()); if (deploymentInfo.getDefaultEncoding() != null) { deployment.setDefaultCharset(Charset.forName(deploymentInfo.getDefaultEncoding())); } if(deploymentInfo.getDefaultRequestEncoding() != null) { deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultRequestEncoding())); } else if (deploymentInfo.getDefaultEncoding() != null) { deployment.setDefaultRequestCharset(Charset.forName(deploymentInfo.getDefaultEncoding())); } if(deploymentInfo.getDefaultResponseEncoding() != null) { deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultResponseEncoding())); } else if (deploymentInfo.getDefaultEncoding() != null) { deployment.setDefaultResponseCharset(Charset.forName(deploymentInfo.getDefaultEncoding())); } handleDeploymentSessionConfig(deploymentInfo, servletContext); deployment.setSessionManager(deploymentInfo.getSessionManagerFactory().createSessionManager(deployment)); deployment.getSessionManager().setDefaultSessionTimeout(deploymentInfo.getDefaultSessionTimeout()); try { deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() { @Override public Void call(HttpServerExchange exchange, Object ignore) throws Exception { final ApplicationListeners listeners = createListeners(); deployment.setApplicationListeners(listeners); //now create the servlets and filters that we know about. We can still get more later createServletsAndFilters(deployment, deploymentInfo); //first initialize the temp dir initializeTempDir(servletContext, deploymentInfo); //then run the SCI's for (final ServletContainerInitializerInfo sci : deploymentInfo.getServletContainerInitializers()) { final InstanceHandle<? extends ServletContainerInitializer> instance = sci.getInstanceFactory().createInstance(); try { instance.getInstance().onStartup(sci.getHandlesTypes(), servletContext); } finally { instance.release(); } } listeners.start(); deployment.getSessionManager().registerSessionListener(new SessionListenerBridge(deployment, listeners, servletContext)); for(SessionListener listener : deploymentInfo.getSessionListeners()) { deployment.getSessionManager().registerSessionListener(listener); } initializeErrorPages(deployment, deploymentInfo); initializeMimeMappings(deployment, deploymentInfo); listeners.contextInitialized(); //run HttpHandler wrappedHandlers = ServletDispatchingHandler.INSTANCE; wrappedHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getInnerHandlerChainWrappers()); wrappedHandlers = new RedirectDirHandler(wrappedHandlers, deployment.getServletPaths()); if(!deploymentInfo.isSecurityDisabled()) { HttpHandler securityHandler = setupSecurityHandlers(wrappedHandlers); wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, securityHandler, wrappedHandlers); } HttpHandler outerHandlers = wrapHandlers(wrappedHandlers, deploymentInfo.getOuterHandlerChainWrappers()); outerHandlers = new SendErrorPageHandler(outerHandlers); wrappedHandlers = new PredicateHandler(DispatcherTypePredicate.REQUEST, outerHandlers, wrappedHandlers); wrappedHandlers = handleDevelopmentModePersistentSessions(wrappedHandlers, deploymentInfo, deployment.getSessionManager(), servletContext); MetricsCollector metrics = deploymentInfo.getMetricsCollector(); if(metrics != null) { wrappedHandlers = new MetricsChainHandler(wrappedHandlers, metrics, deployment); } if( deploymentInfo.getCrawlerSessionManagerConfig() != null ) { wrappedHandlers = new CrawlerSessionManagerHandler(deploymentInfo.getCrawlerSessionManagerConfig(), wrappedHandlers); } final ServletInitialHandler servletInitialHandler = SecurityActions.createServletInitialHandler(deployment.getServletPaths(), wrappedHandlers, deployment, servletContext); HttpHandler initialHandler = wrapHandlers(servletInitialHandler, deployment.getDeploymentInfo().getInitialHandlerChainWrappers()); initialHandler = new HttpContinueReadHandler(initialHandler); if(deploymentInfo.getUrlEncoding() != null) { initialHandler = Handlers.urlDecodingHandler(deploymentInfo.getUrlEncoding(), initialHandler); } deployment.setInitialHandler(initialHandler); deployment.setServletHandler(servletInitialHandler); deployment.getServletPaths().invalidate(); //make sure we have a fresh set of servlet paths servletContext.initDone(); return null; } }).call(null, null); } catch (Exception e) { throw new RuntimeException(e); } //any problems with the paths won't get detected until the data is initialize //so we force initialization here deployment.getServletPaths().initData(); for(ServletContextListener listener : deploymentInfo.getDeploymentCompleteListeners()) { listener.contextInitialized(new ServletContextEvent(servletContext)); } state = State.DEPLOYED; } private void createServletsAndFilters(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) { for (Map.Entry<String, ServletInfo> servlet : deploymentInfo.getServlets().entrySet()) { deployment.getServlets().addServlet(servlet.getValue()); } for (Map.Entry<String, FilterInfo> filter : deploymentInfo.getFilters().entrySet()) { deployment.getFilters().addFilter(filter.getValue()); } } private void handleExtensions(final DeploymentInfo deploymentInfo, final ServletContextImpl servletContext) { Set<String> loadedExtensions = new HashSet<>(); for (ServletExtension extension : ServiceLoader.load(ServletExtension.class, deploymentInfo.getClassLoader())) { loadedExtensions.add(extension.getClass().getName()); extension.handleDeployment(deploymentInfo, servletContext); } if (ServletExtension.class.getClassLoader() != null && !ServletExtension.class.getClassLoader().equals(deploymentInfo.getClassLoader())) { for (ServletExtension extension : ServiceLoader.load(ServletExtension.class)) { // Note: If the CLs are different, but can the see the same extensions and extension might get loaded // and thus instantiated twice, but the handleDeployment() is executed only once. if (!loadedExtensions.contains(extension.getClass().getName())) { extension.handleDeployment(deploymentInfo, servletContext); } } } for (ServletExtension extension : ServletExtensionHolder.getServletExtensions()) { if (!loadedExtensions.contains(extension.getClass().getName())) { extension.handleDeployment(deploymentInfo, servletContext); } } for(ServletExtension extension : deploymentInfo.getServletExtensions()) { extension.handleDeployment(deploymentInfo, servletContext); } }
sets up the outer security handlers.

the handler that actually performs the access check happens later in the chain, it is not setup here

Params:
  • initialHandler – The handler to wrap with security handlers
/** * sets up the outer security handlers. * <p> * the handler that actually performs the access check happens later in the chain, it is not setup here * * @param initialHandler The handler to wrap with security handlers */
private HttpHandler setupSecurityHandlers(HttpHandler initialHandler) { final DeploymentInfo deploymentInfo = deployment.getDeploymentInfo(); final LoginConfig loginConfig = deploymentInfo.getLoginConfig(); HttpHandler current = initialHandler; current = new SSLInformationAssociationHandler(current); final SecurityPathMatches securityPathMatches = buildSecurityConstraints(); securityPathMatches.logWarningsAboutUncoveredMethods(); current = new ServletAuthenticationCallHandler(current); for(HandlerWrapper wrapper : deploymentInfo.getSecurityWrappers()) { current = wrapper.wrap(current); } if(deploymentInfo.isDisableCachingForSecuredPages()) { current = Handlers.predicate(Predicates.authRequired(), Handlers.disableCache(current), current); } if (!securityPathMatches.isEmpty()) { current = new ServletAuthenticationConstraintHandler(current); } current = new ServletConfidentialityConstraintHandler(deploymentInfo.getConfidentialPortManager(), current); if (!securityPathMatches.isEmpty()) { current = new ServletSecurityConstraintHandler(securityPathMatches, current); } HandlerWrapper initialSecurityWrapper = deploymentInfo.getInitialSecurityWrapper(); String mechName = null; if (initialSecurityWrapper == null) { final Map<String, AuthenticationMechanismFactory> factoryMap = new HashMap<>(deploymentInfo.getAuthenticationMechanisms()); final IdentityManager identityManager = deploymentInfo.getIdentityManager(); if(!factoryMap.containsKey(BASIC_AUTH)) { factoryMap.put(BASIC_AUTH, BasicAuthenticationMechanism.FACTORY); } if(!factoryMap.containsKey(FORM_AUTH)) { factoryMap.put(FORM_AUTH, ServletFormAuthenticationMechanism.FACTORY); } if(!factoryMap.containsKey(DIGEST_AUTH)) { factoryMap.put(DIGEST_AUTH, DigestAuthenticationMechanism.FACTORY); } if(!factoryMap.containsKey(CLIENT_CERT_AUTH)) { factoryMap.put(CLIENT_CERT_AUTH, ClientCertAuthenticationMechanism.FACTORY); } if(!factoryMap.containsKey(ExternalAuthenticationMechanism.NAME)) { factoryMap.put(ExternalAuthenticationMechanism.NAME, ExternalAuthenticationMechanism.FACTORY); } if(!factoryMap.containsKey(GenericHeaderAuthenticationMechanism.NAME)) { factoryMap.put(GenericHeaderAuthenticationMechanism.NAME, GenericHeaderAuthenticationMechanism.FACTORY); } List<AuthenticationMechanism> authenticationMechanisms = new LinkedList<>(); if(deploymentInfo.isUseCachedAuthenticationMechanism()) { authenticationMechanisms.add(new CachedAuthenticatedSessionMechanism(identityManager)); } if (loginConfig != null || deploymentInfo.getJaspiAuthenticationMechanism() != null) { //we don't allow multipart requests, and use the default encoding when it's set FormEncodedDataDefinition formEncodedDataDefinition = new FormEncodedDataDefinition(); String reqEncoding = deploymentInfo.getDefaultRequestEncoding(); if(reqEncoding == null) { reqEncoding = deploymentInfo.getDefaultEncoding(); } if (reqEncoding != null) { formEncodedDataDefinition.setDefaultEncoding(reqEncoding); } FormParserFactory parser = FormParserFactory.builder(false) .addParser(formEncodedDataDefinition) .build(); List<AuthMethodConfig> authMethods = Collections.<AuthMethodConfig>emptyList(); if(loginConfig != null) { authMethods = loginConfig.getAuthMethods(); } for(AuthMethodConfig method : authMethods) { AuthenticationMechanismFactory factory = factoryMap.get(method.getName()); if(factory == null) { throw UndertowServletMessages.MESSAGES.unknownAuthenticationMechanism(method.getName()); } if(mechName == null) { mechName = method.getName(); } final Map<String, String> properties = new HashMap<>(); properties.put(AuthenticationMechanismFactory.CONTEXT_PATH, deploymentInfo.getContextPath()); properties.put(AuthenticationMechanismFactory.REALM, loginConfig.getRealmName()); properties.put(AuthenticationMechanismFactory.ERROR_PAGE, loginConfig.getErrorPage()); properties.put(AuthenticationMechanismFactory.LOGIN_PAGE, loginConfig.getLoginPage()); properties.putAll(method.getProperties()); String name = method.getName().toUpperCase(Locale.US); // The mechanism name is passed in from the HttpServletRequest interface as the name reported needs to be // comparable using '==' name = name.equals(FORM_AUTH) ? FORM_AUTH : name; name = name.equals(BASIC_AUTH) ? BASIC_AUTH : name; name = name.equals(DIGEST_AUTH) ? DIGEST_AUTH : name; name = name.equals(CLIENT_CERT_AUTH) ? CLIENT_CERT_AUTH : name; authenticationMechanisms.add(factory.create(name, identityManager, parser, properties)); } } deployment.setAuthenticationMechanisms(authenticationMechanisms); //if the JASPI auth mechanism is set then it takes over if(deploymentInfo.getJaspiAuthenticationMechanism() == null) { current = new AuthenticationMechanismsHandler(current, authenticationMechanisms); } else { current = new AuthenticationMechanismsHandler(current, Collections.<AuthenticationMechanism>singletonList(deploymentInfo.getJaspiAuthenticationMechanism())); } current = new CachedAuthenticatedSessionHandler(current, this.deployment.getServletContext()); } List<NotificationReceiver> notificationReceivers = deploymentInfo.getNotificationReceivers(); if (!notificationReceivers.isEmpty()) { current = new NotificationReceiverHandler(current, notificationReceivers); } if (initialSecurityWrapper == null) { // TODO - A switch to constraint driven could be configurable, however before we can support that with servlets we would // need additional tracking within sessions if a servlet has specifically requested that authentication occurs. SecurityContextFactory contextFactory = deploymentInfo.getSecurityContextFactory(); if (contextFactory == null) { contextFactory = SecurityContextFactoryImpl.INSTANCE; } current = new SecurityInitialHandler(deploymentInfo.getAuthenticationMode(), deploymentInfo.getIdentityManager(), mechName, contextFactory, current); } else { current = initialSecurityWrapper.wrap(current); } return current; } private SecurityPathMatches buildSecurityConstraints() { SecurityPathMatches.Builder builder = SecurityPathMatches.builder(deployment.getDeploymentInfo()); final Set<String> urlPatterns = new HashSet<>(); for (SecurityConstraint constraint : deployment.getDeploymentInfo().getSecurityConstraints()) { builder.addSecurityConstraint(constraint); for (WebResourceCollection webResources : constraint.getWebResourceCollections()) { urlPatterns.addAll(webResources.getUrlPatterns()); } } for (final ServletInfo servlet : deployment.getDeploymentInfo().getServlets().values()) { final ServletSecurityInfo securityInfo = servlet.getServletSecurityInfo(); if (securityInfo != null) { final Set<String> mappings = new HashSet<>(servlet.getMappings()); mappings.removeAll(urlPatterns); if (!mappings.isEmpty()) { final Set<String> methods = new HashSet<>(); for (HttpMethodSecurityInfo method : securityInfo.getHttpMethodSecurityInfo()) { methods.add(method.getMethod()); if (method.getRolesAllowed().isEmpty() && method.getEmptyRoleSemantic() == EmptyRoleSemantic.PERMIT) { //this is an implict allow continue; } SecurityConstraint newConstraint = new SecurityConstraint() .addRolesAllowed(method.getRolesAllowed()) .setTransportGuaranteeType(method.getTransportGuaranteeType()) .addWebResourceCollection(new WebResourceCollection().addUrlPatterns(mappings) .addHttpMethod(method.getMethod())); builder.addSecurityConstraint(newConstraint); } //now add the constraint, unless it has all default values and method constrains where specified if (!securityInfo.getRolesAllowed().isEmpty() || securityInfo.getEmptyRoleSemantic() != EmptyRoleSemantic.PERMIT || methods.isEmpty()) { SecurityConstraint newConstraint = new SecurityConstraint() .setEmptyRoleSemantic(securityInfo.getEmptyRoleSemantic()) .addRolesAllowed(securityInfo.getRolesAllowed()) .setTransportGuaranteeType(securityInfo.getTransportGuaranteeType()) .addWebResourceCollection(new WebResourceCollection().addUrlPatterns(mappings) .addHttpMethodOmissions(methods)); builder.addSecurityConstraint(newConstraint); } } } } return builder.build(); } private void initializeTempDir(final ServletContextImpl servletContext, final DeploymentInfo deploymentInfo) { if (deploymentInfo.getTempDir() != null) { servletContext.setAttribute(ServletContext.TEMPDIR, deploymentInfo.getTempDir()); } else { servletContext.setAttribute(ServletContext.TEMPDIR, new File(SecurityActions.getSystemProperty("java.io.tmpdir"))); } } private void initializeMimeMappings(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) { final Map<String, String> mappings = new HashMap<>(MimeMappings.DEFAULT_MIME_MAPPINGS); for (MimeMapping mapping : deploymentInfo.getMimeMappings()) { mappings.put(mapping.getExtension().toLowerCase(Locale.ENGLISH), mapping.getMimeType()); } deployment.setMimeExtensionMappings(mappings); } private void initializeErrorPages(final DeploymentImpl deployment, final DeploymentInfo deploymentInfo) { final Map<Integer, String> codes = new HashMap<>(); final Map<Class<? extends Throwable>, String> exceptions = new HashMap<>(); String defaultErrorPage = null; for (final ErrorPage page : deploymentInfo.getErrorPages()) { if (page.getExceptionType() != null) { exceptions.put(page.getExceptionType(), page.getLocation()); } else if (page.getErrorCode() != null) { codes.put(page.getErrorCode(), page.getLocation()); } else { if (defaultErrorPage != null) { throw UndertowServletMessages.MESSAGES.moreThanOneDefaultErrorPage(defaultErrorPage, page.getLocation()); } else { defaultErrorPage = page.getLocation(); } } } deployment.setErrorPages(new ErrorPages(codes, exceptions, defaultErrorPage)); } private ApplicationListeners createListeners() { final List<ManagedListener> managedListeners = new ArrayList<>(); for (final ListenerInfo listener : deployment.getDeploymentInfo().getListeners()) { managedListeners.add(new ManagedListener(listener, listener.isProgramatic())); } return new ApplicationListeners(managedListeners, deployment.getServletContext()); } private static HttpHandler wrapHandlers(final HttpHandler wrapee, final List<HandlerWrapper> wrappers) { HttpHandler current = wrapee; for (HandlerWrapper wrapper : wrappers) { current = wrapper.wrap(current); } return current; } @Override public HttpHandler start() throws ServletException { try { return deployment.createThreadSetupAction(new ThreadSetupHandler.Action<HttpHandler, Object>() { @Override public HttpHandler call(HttpServerExchange exchange, Object ignore) throws ServletException { deployment.getSessionManager().start(); //we need to copy before iterating //because listeners can add other listeners ArrayList<Lifecycle> lifecycles = new ArrayList<>(deployment.getLifecycleObjects()); for (Lifecycle object : lifecycles) { object.start(); } HttpHandler root = deployment.getHandler(); final TreeMap<Integer, List<ManagedServlet>> loadOnStartup = new TreeMap<>(); for (Map.Entry<String, ServletHandler> entry : deployment.getServlets().getServletHandlers().entrySet()) { ManagedServlet servlet = entry.getValue().getManagedServlet(); Integer loadOnStartupNumber = servlet.getServletInfo().getLoadOnStartup(); if (loadOnStartupNumber != null) { if (loadOnStartupNumber < 0) { continue; } List<ManagedServlet> list = loadOnStartup.get(loadOnStartupNumber); if (list == null) { loadOnStartup.put(loadOnStartupNumber, list = new ArrayList<>()); } list.add(servlet); } } for (Map.Entry<Integer, List<ManagedServlet>> load : loadOnStartup.entrySet()) { for (ManagedServlet servlet : load.getValue()) { servlet.createServlet(); } } if (deployment.getDeploymentInfo().isEagerFilterInit()) { for (ManagedFilter filter : deployment.getFilters().getFilters().values()) { filter.createFilter(); } } state = State.STARTED; return root; } }).call(null, null); } catch (ServletException|RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } @Override public void stop() throws ServletException { try { deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() { @Override public Void call(HttpServerExchange exchange, Object ignore) throws ServletException { for (Lifecycle object : deployment.getLifecycleObjects()) { try { object.stop(); } catch (Throwable t) { UndertowServletLogger.ROOT_LOGGER.failedToDestroy(object, t); } } deployment.getSessionManager().stop(); state = State.DEPLOYED; return null; } }).call(null, null); } catch (ServletException|RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } private HttpHandler handleDevelopmentModePersistentSessions(HttpHandler next, final DeploymentInfo deploymentInfo, final SessionManager sessionManager, final ServletContextImpl servletContext) { final SessionPersistenceManager sessionPersistenceManager = deploymentInfo.getSessionPersistenceManager(); if (sessionPersistenceManager != null) { SessionRestoringHandler handler = new SessionRestoringHandler(deployment.getDeploymentInfo().getDeploymentName(), sessionManager, servletContext, next, sessionPersistenceManager); deployment.addLifecycleObjects(handler); return handler; } return next; } public void handleDeploymentSessionConfig(DeploymentInfo deploymentInfo, ServletContextImpl servletContext) { SessionCookieConfigImpl sessionCookieConfig = servletContext.getSessionCookieConfig(); ServletSessionConfig sc = deploymentInfo.getServletSessionConfig(); if (sc != null) { sessionCookieConfig.setName(sc.getName()); sessionCookieConfig.setComment(sc.getComment()); sessionCookieConfig.setDomain(sc.getDomain()); sessionCookieConfig.setHttpOnly(sc.isHttpOnly()); sessionCookieConfig.setMaxAge(sc.getMaxAge()); if(sc.getPath() != null) { sessionCookieConfig.setPath(sc.getPath()); } else { sessionCookieConfig.setPath(deploymentInfo.getContextPath()); } sessionCookieConfig.setSecure(sc.isSecure()); if (sc.getSessionTrackingModes() != null) { servletContext.setDefaultSessionTrackingModes(new HashSet<>(sc.getSessionTrackingModes())); } } } @Override public void undeploy() { try { deployment.createThreadSetupAction(new ThreadSetupHandler.Action<Void, Object>() { @Override public Void call(HttpServerExchange exchange, Object ignore) throws ServletException { for(ServletContextListener listener : deployment.getDeploymentInfo().getDeploymentCompleteListeners()) { try { listener.contextDestroyed(new ServletContextEvent(deployment.getServletContext())); } catch (Throwable t) { UndertowServletLogger.REQUEST_LOGGER.failedToDestroy(listener, t); } } deployment.destroy(); deployment = null; state = State.UNDEPLOYED; return null; } }).call(null, null); } catch (Exception e) { throw new RuntimeException(e); } } @Override public State getState() { return state; } @Override public Deployment getDeployment() { return deployment; } }