All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory Maven / Gradle / Ivy

There is a newer version: 3.2.5
Show newest version
/*
 * Copyright 2012-2018 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.boot.context.embedded.undertow;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;

import io.undertow.Undertow;
import io.undertow.Undertow.Builder;
import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.AccessLogReceiver;
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
import io.undertow.server.handlers.resource.FileResourceManager;
import io.undertow.server.handlers.resource.Resource;
import io.undertow.server.handlers.resource.ResourceChangeListener;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.server.handlers.resource.URLResource;
import io.undertow.server.session.SessionManager;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.ListenerInfo;
import io.undertow.servlet.api.MimeMapping;
import io.undertow.servlet.api.ServletContainerInitializerInfo;
import io.undertow.servlet.api.ServletStackTraces;
import io.undertow.servlet.handlers.DefaultServlet;
import io.undertow.servlet.util.ImmediateInstanceFactory;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Sequence;
import org.xnio.SslClientAuthMode;
import org.xnio.Xnio;
import org.xnio.XnioWorker;

import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.MimeMappings.Mapping;
import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;

/**
 * {@link EmbeddedServletContainerFactory} that can be used to create
 * {@link UndertowEmbeddedServletContainer}s.
 * 

* Unless explicitly configured otherwise, the factory will create containers that listen * for HTTP requests on port 8080. * * @author Ivan Sopov * @author Andy Wilkinson * @author Marcos Barbero * @author Eddú Meléndez * @since 1.2.0 * @see UndertowEmbeddedServletContainer */ public class UndertowEmbeddedServletContainerFactory extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware { private static final Set> NO_CLASSES = Collections.emptySet(); private List builderCustomizers = new ArrayList(); private List deploymentInfoCustomizers = new ArrayList(); private ResourceLoader resourceLoader; private Integer bufferSize; private Integer ioThreads; private Integer workerThreads; private Boolean directBuffers; private File accessLogDirectory; private String accessLogPattern; private String accessLogPrefix; private String accessLogSuffix; private boolean accessLogEnabled = false; private boolean accessLogRotate = true; private boolean useForwardHeaders; /** * Create a new {@link UndertowEmbeddedServletContainerFactory} instance. */ public UndertowEmbeddedServletContainerFactory() { super(); getJspServlet().setRegistered(false); } /** * Create a new {@link UndertowEmbeddedServletContainerFactory} that listens for * requests using the specified port. * @param port the port to listen on */ public UndertowEmbeddedServletContainerFactory(int port) { super(port); getJspServlet().setRegistered(false); } /** * Create a new {@link UndertowEmbeddedServletContainerFactory} with the specified * context path and port. * @param contextPath the root context path * @param port the port to listen on */ public UndertowEmbeddedServletContainerFactory(String contextPath, int port) { super(contextPath, port); getJspServlet().setRegistered(false); } /** * Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow * {@link Builder}. Calling this method will replace any existing customizers. * @param customizers the customizers to set */ public void setBuilderCustomizers( Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.builderCustomizers = new ArrayList(customizers); } /** * Returns a mutable collection of the {@link UndertowBuilderCustomizer}s that will be * applied to the Undertow {@link Builder} . * @return the customizers that will be applied */ public Collection getBuilderCustomizers() { return this.builderCustomizers; } /** * Add {@link UndertowBuilderCustomizer}s that should be used to customize the * Undertow {@link Builder}. * @param customizers the customizers to add */ public void addBuilderCustomizers(UndertowBuilderCustomizer... customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.builderCustomizers.addAll(Arrays.asList(customizers)); } /** * Set {@link UndertowDeploymentInfoCustomizer}s that should be applied to the * Undertow {@link DeploymentInfo}. Calling this method will replace any existing * customizers. * @param customizers the customizers to set */ public void setDeploymentInfoCustomizers( Collection customizers) { Assert.notNull(customizers, "Customizers must not be null"); this.deploymentInfoCustomizers = new ArrayList( customizers); } /** * Returns a mutable collection of the {@link UndertowDeploymentInfoCustomizer}s that * will be applied to the Undertow {@link DeploymentInfo} . * @return the customizers that will be applied */ public Collection getDeploymentInfoCustomizers() { return this.deploymentInfoCustomizers; } /** * Add {@link UndertowDeploymentInfoCustomizer}s that should be used to customize the * Undertow {@link DeploymentInfo}. * @param customizers the customizers to add */ public void addDeploymentInfoCustomizers( UndertowDeploymentInfoCustomizer... customizers) { Assert.notNull(customizers, "UndertowDeploymentInfoCustomizers must not be null"); this.deploymentInfoCustomizers.addAll(Arrays.asList(customizers)); } @Override public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { DeploymentManager manager = createDeploymentManager(initializers); int port = getPort(); Builder builder = createBuilder(port); return getUndertowEmbeddedServletContainer(builder, manager, port); } private Builder createBuilder(int port) { Builder builder = Undertow.builder(); if (this.bufferSize != null) { builder.setBufferSize(this.bufferSize); } if (this.ioThreads != null) { builder.setIoThreads(this.ioThreads); } if (this.workerThreads != null) { builder.setWorkerThreads(this.workerThreads); } if (this.directBuffers != null) { builder.setDirectBuffers(this.directBuffers); } if (getSsl() != null && getSsl().isEnabled()) { configureSsl(getSsl(), port, builder); } else { builder.addHttpListener(port, getListenAddress()); } for (UndertowBuilderCustomizer customizer : this.builderCustomizers) { customizer.customize(builder); } return builder; } private void configureSsl(Ssl ssl, int port, Builder builder) { try { SSLContext sslContext = SSLContext.getInstance(ssl.getProtocol()); sslContext.init(getKeyManagers(), getTrustManagers(), null); builder.addHttpsListener(port, getListenAddress(), sslContext); builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, getSslClientAuthMode(ssl)); if (ssl.getEnabledProtocols() != null) { builder.setSocketOption(Options.SSL_ENABLED_PROTOCOLS, Sequence.of(ssl.getEnabledProtocols())); } if (ssl.getCiphers() != null) { builder.setSocketOption(Options.SSL_ENABLED_CIPHER_SUITES, Sequence.of(ssl.getCiphers())); } } catch (NoSuchAlgorithmException ex) { throw new IllegalStateException(ex); } catch (KeyManagementException ex) { throw new IllegalStateException(ex); } } private String getListenAddress() { if (getAddress() == null) { return "0.0.0.0"; } return getAddress().getHostAddress(); } private SslClientAuthMode getSslClientAuthMode(Ssl ssl) { if (ssl.getClientAuth() == ClientAuth.NEED) { return SslClientAuthMode.REQUIRED; } if (ssl.getClientAuth() == ClientAuth.WANT) { return SslClientAuthMode.REQUESTED; } return SslClientAuthMode.NOT_REQUESTED; } private KeyManager[] getKeyManagers() { try { KeyStore keyStore = getKeyStore(); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); Ssl ssl = getSsl(); char[] keyPassword = (ssl.getKeyPassword() != null) ? ssl.getKeyPassword().toCharArray() : null; if (keyPassword == null && ssl.getKeyStorePassword() != null) { keyPassword = ssl.getKeyStorePassword().toCharArray(); } keyManagerFactory.init(keyStore, keyPassword); if (ssl.getKeyAlias() != null) { return getConfigurableAliasKeyManagers(ssl, keyManagerFactory.getKeyManagers()); } return keyManagerFactory.getKeyManagers(); } catch (Exception ex) { throw new IllegalStateException(ex); } } private KeyManager[] getConfigurableAliasKeyManagers(Ssl ssl, KeyManager[] keyManagers) { for (int i = 0; i < keyManagers.length; i++) { if (keyManagers[i] instanceof X509ExtendedKeyManager) { keyManagers[i] = new ConfigurableAliasKeyManager( (X509ExtendedKeyManager) keyManagers[i], ssl.getKeyAlias()); } } return keyManagers; } private KeyStore getKeyStore() throws Exception { if (getSslStoreProvider() != null) { return getSslStoreProvider().getKeyStore(); } Ssl ssl = getSsl(); return loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStoreProvider(), ssl.getKeyStore(), ssl.getKeyStorePassword()); } private TrustManager[] getTrustManagers() { try { KeyStore store = getTrustStore(); TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(store); return trustManagerFactory.getTrustManagers(); } catch (Exception ex) { throw new IllegalStateException(ex); } } private KeyStore getTrustStore() throws Exception { if (getSslStoreProvider() != null) { return getSslStoreProvider().getTrustStore(); } Ssl ssl = getSsl(); return loadKeyStore(ssl.getTrustStoreType(), ssl.getTrustStoreProvider(), ssl.getTrustStore(), ssl.getTrustStorePassword()); } private KeyStore loadKeyStore(String type, String provider, String resource, String password) throws Exception { type = (type != null) ? type : "JKS"; if (resource == null) { return null; } KeyStore store = (provider != null) ? KeyStore.getInstance(type, provider) : KeyStore.getInstance(type); URL url = ResourceUtils.getURL(resource); store.load(url.openStream(), (password != null) ? password.toCharArray() : null); return store; } private DeploymentManager createDeploymentManager( ServletContextInitializer... initializers) { DeploymentInfo deployment = Servlets.deployment(); registerServletContainerInitializerToDriveServletContextInitializers(deployment, initializers); deployment.setClassLoader(getServletClassLoader()); deployment.setContextPath(getContextPath()); deployment.setDisplayName(getDisplayName()); deployment.setDeploymentName("spring-boot"); if (isRegisterDefaultServlet()) { deployment.addServlet(Servlets.servlet("default", DefaultServlet.class)); } configureErrorPages(deployment); deployment.setServletStackTraces(ServletStackTraces.NONE); deployment.setResourceManager(getDocumentRootResourceManager()); configureMimeMappings(deployment); for (UndertowDeploymentInfoCustomizer customizer : this.deploymentInfoCustomizers) { customizer.customize(deployment); } if (isAccessLogEnabled()) { configureAccessLog(deployment); } if (isPersistSession()) { File dir = getValidSessionStoreDir(); deployment.setSessionPersistenceManager(new FileSessionPersistence(dir)); } addLocaleMappings(deployment); DeploymentManager manager = Servlets.newContainer().addDeployment(deployment); manager.deploy(); SessionManager sessionManager = manager.getDeployment().getSessionManager(); int sessionTimeout = (getSessionTimeout() > 0) ? getSessionTimeout() : -1; sessionManager.setDefaultSessionTimeout(sessionTimeout); return manager; } private void configureAccessLog(DeploymentInfo deploymentInfo) { try { createAccessLogDirectoryIfNecessary(); XnioWorker worker = createWorker(); String prefix = (this.accessLogPrefix != null) ? this.accessLogPrefix : "access_log."; final DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver( worker, this.accessLogDirectory, prefix, this.accessLogSuffix, this.accessLogRotate); EventListener listener = new AccessLogShutdownListener(worker, accessLogReceiver); deploymentInfo.addListener(new ListenerInfo(AccessLogShutdownListener.class, new ImmediateInstanceFactory(listener))); deploymentInfo.addInitialHandlerChainWrapper(new HandlerWrapper() { @Override public HttpHandler wrap(HttpHandler handler) { return createAccessLogHandler(handler, accessLogReceiver); } }); } catch (IOException ex) { throw new IllegalStateException("Failed to create AccessLogHandler", ex); } } private AccessLogHandler createAccessLogHandler(HttpHandler handler, AccessLogReceiver accessLogReceiver) { createAccessLogDirectoryIfNecessary(); String formatString = (this.accessLogPattern != null) ? this.accessLogPattern : "common"; return new AccessLogHandler(handler, accessLogReceiver, formatString, Undertow.class.getClassLoader()); } private void createAccessLogDirectoryIfNecessary() { Assert.state(this.accessLogDirectory != null, "Access log directory is not set"); if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) { throw new IllegalStateException("Failed to create access log directory '" + this.accessLogDirectory + "'"); } } private XnioWorker createWorker() throws IOException { Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader()); return xnio.createWorker( OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap()); } private void addLocaleMappings(DeploymentInfo deployment) { for (Map.Entry entry : getLocaleCharsetMappings().entrySet()) { Locale locale = entry.getKey(); Charset charset = entry.getValue(); deployment.addLocaleCharsetMapping(locale.toString(), charset.toString()); } } private void registerServletContainerInitializerToDriveServletContextInitializers( DeploymentInfo deployment, ServletContextInitializer... initializers) { ServletContextInitializer[] mergedInitializers = mergeInitializers(initializers); Initializer initializer = new Initializer(mergedInitializers); deployment.addServletContainerInitializer(new ServletContainerInitializerInfo( Initializer.class, new ImmediateInstanceFactory(initializer), NO_CLASSES)); } private ClassLoader getServletClassLoader() { if (this.resourceLoader != null) { return this.resourceLoader.getClassLoader(); } return getClass().getClassLoader(); } private ResourceManager getDocumentRootResourceManager() { File root = getCanonicalDocumentRoot(); List metaInfResourceUrls = getUrlsOfJarsWithMetaInfResources(); List resourceJarUrls = new ArrayList(); List resourceManagers = new ArrayList(); ResourceManager rootResourceManager = (root.isDirectory() ? new FileResourceManager(root, 0) : new JarResourceManager(root)); resourceManagers.add(rootResourceManager); for (URL url : metaInfResourceUrls) { if ("file".equals(url.getProtocol())) { try { File file = new File(url.toURI()); if (file.isFile()) { resourceJarUrls.add(new URL("jar:" + url + "!/")); } else { resourceManagers.add(new FileResourceManager( new File(file, "META-INF/resources"), 0)); } } catch (Exception ex) { throw new RuntimeException(ex); } } else { resourceJarUrls.add(url); } } resourceManagers.add(new MetaInfResourcesResourceManager(resourceJarUrls)); return new CompositeResourceManager( resourceManagers.toArray(new ResourceManager[resourceManagers.size()])); } /** * Return the document root in canonical form. Undertow uses File#getCanonicalFile() * to determine whether a resource has been requested using the proper case but on * Windows {@code java.io.tmpdir} may be set as a tilde-compressed pathname. * @return the canonical document root */ private File getCanonicalDocumentRoot() { try { File root = getValidDocumentRoot(); root = (root != null) ? root : createTempDir("undertow-docbase"); return root.getCanonicalFile(); } catch (IOException ex) { throw new IllegalStateException("Cannot get canonical document root", ex); } } private void configureErrorPages(DeploymentInfo servletBuilder) { for (ErrorPage errorPage : getErrorPages()) { servletBuilder.addErrorPage(getUndertowErrorPage(errorPage)); } } private io.undertow.servlet.api.ErrorPage getUndertowErrorPage(ErrorPage errorPage) { if (errorPage.getStatus() != null) { return new io.undertow.servlet.api.ErrorPage(errorPage.getPath(), errorPage.getStatusCode()); } if (errorPage.getException() != null) { return new io.undertow.servlet.api.ErrorPage(errorPage.getPath(), errorPage.getException()); } return new io.undertow.servlet.api.ErrorPage(errorPage.getPath()); } private void configureMimeMappings(DeploymentInfo servletBuilder) { for (Mapping mimeMapping : getMimeMappings()) { servletBuilder.addMimeMapping(new MimeMapping(mimeMapping.getExtension(), mimeMapping.getMimeType())); } } /** * Factory method called to create the {@link UndertowEmbeddedServletContainer}. * Subclasses can override this method to return a different * {@link UndertowEmbeddedServletContainer} or apply additional processing to the * {@link Builder} and {@link DeploymentManager} used to bootstrap Undertow * @param builder the builder * @param manager the deployment manager * @param port the port that Undertow should listen on * @return a new {@link UndertowEmbeddedServletContainer} instance */ protected UndertowEmbeddedServletContainer getUndertowEmbeddedServletContainer( Builder builder, DeploymentManager manager, int port) { return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(), isUseForwardHeaders(), port >= 0, getCompression(), getServerHeader()); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public void setBufferSize(Integer bufferSize) { this.bufferSize = bufferSize; } @Deprecated public void setBuffersPerRegion(Integer buffersPerRegion) { } public void setIoThreads(Integer ioThreads) { this.ioThreads = ioThreads; } public void setWorkerThreads(Integer workerThreads) { this.workerThreads = workerThreads; } public void setDirectBuffers(Boolean directBuffers) { this.directBuffers = directBuffers; } public void setAccessLogDirectory(File accessLogDirectory) { this.accessLogDirectory = accessLogDirectory; } public void setAccessLogPattern(String accessLogPattern) { this.accessLogPattern = accessLogPattern; } public String getAccessLogPrefix() { return this.accessLogPrefix; } public void setAccessLogPrefix(String accessLogPrefix) { this.accessLogPrefix = accessLogPrefix; } public void setAccessLogSuffix(String accessLogSuffix) { this.accessLogSuffix = accessLogSuffix; } public void setAccessLogEnabled(boolean accessLogEnabled) { this.accessLogEnabled = accessLogEnabled; } public boolean isAccessLogEnabled() { return this.accessLogEnabled; } public void setAccessLogRotate(boolean accessLogRotate) { this.accessLogRotate = accessLogRotate; } protected final boolean isUseForwardHeaders() { return this.useForwardHeaders; } /** * Set if x-forward-* headers should be processed. * @param useForwardHeaders if x-forward headers should be used * @since 1.3.0 */ public void setUseForwardHeaders(boolean useForwardHeaders) { this.useForwardHeaders = useForwardHeaders; } /** * {@link ResourceManager} that exposes resource in {@code META-INF/resources} * directory of nested (in {@code BOOT-INF/lib} or {@code WEB-INF/lib}) jars. */ private static final class MetaInfResourcesResourceManager implements ResourceManager { private final List metaInfResourceJarUrls; private MetaInfResourcesResourceManager(List metaInfResourceJarUrls) { this.metaInfResourceJarUrls = metaInfResourceJarUrls; } @Override public void close() throws IOException { } @Override public Resource getResource(String path) { for (URL url : this.metaInfResourceJarUrls) { URLResource resource = getMetaInfResource(url, path); if (resource != null) { return resource; } } return null; } @Override public boolean isResourceChangeListenerSupported() { return false; } @Override public void registerResourceChangeListener(ResourceChangeListener listener) { } @Override public void removeResourceChangeListener(ResourceChangeListener listener) { } private URLResource getMetaInfResource(URL resourceJar, String path) { try { URL resourceUrl = new URL(resourceJar + "META-INF/resources" + path); URLResource resource = new URLResource(resourceUrl, path); if (resource.getContentLength() < 0) { return null; } return resource; } catch (MalformedURLException ex) { return null; } } } /** * {@link ServletContainerInitializer} to initialize {@link ServletContextInitializer * ServletContextInitializers}. */ private static class Initializer implements ServletContainerInitializer { private final ServletContextInitializer[] initializers; Initializer(ServletContextInitializer[] initializers) { this.initializers = initializers; } @Override public void onStartup(Set> classes, ServletContext servletContext) throws ServletException { for (ServletContextInitializer initializer : this.initializers) { initializer.onStartup(servletContext); } } } /** * {@link X509ExtendedKeyManager} that supports custom alias configuration. */ private static class ConfigurableAliasKeyManager extends X509ExtendedKeyManager { private final X509ExtendedKeyManager keyManager; private final String alias; ConfigurableAliasKeyManager(X509ExtendedKeyManager keyManager, String alias) { this.keyManager = keyManager; this.alias = alias; } @Override public String chooseEngineClientAlias(String[] strings, Principal[] principals, SSLEngine sslEngine) { return this.keyManager.chooseEngineClientAlias(strings, principals, sslEngine); } @Override public String chooseEngineServerAlias(String s, Principal[] principals, SSLEngine sslEngine) { if (this.alias == null) { return this.keyManager.chooseEngineServerAlias(s, principals, sslEngine); } return this.alias; } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return this.keyManager.chooseClientAlias(keyType, issuers, socket); } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return this.keyManager.chooseServerAlias(keyType, issuers, socket); } @Override public X509Certificate[] getCertificateChain(String alias) { return this.keyManager.getCertificateChain(alias); } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return this.keyManager.getClientAliases(keyType, issuers); } @Override public PrivateKey getPrivateKey(String alias) { return this.keyManager.getPrivateKey(alias); } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return this.keyManager.getServerAliases(keyType, issuers); } } private static class AccessLogShutdownListener implements ServletContextListener { private final XnioWorker worker; private final DefaultAccessLogReceiver accessLogReceiver; AccessLogShutdownListener(XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver) { this.worker = worker; this.accessLogReceiver = accessLogReceiver; } @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { try { this.accessLogReceiver.close(); this.worker.shutdown(); } catch (IOException ex) { throw new IllegalStateException(ex); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy