/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.undertow.deployment; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import io.undertow.server.HandlerWrapper; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.servlet.api.ConfidentialPortManager; import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentManager; import io.undertow.servlet.api.ThreadSetupAction; import io.undertow.servlet.core.CompositeThreadSetupAction; import io.undertow.servlet.core.ContextClassLoaderSetupAction; import org.jboss.as.clustering.ClassLoaderAwareClassResolver; import org.jboss.as.clustering.web.DistributedCacheManagerFactory; import org.jboss.as.clustering.web.OutgoingDistributableSessionData; import org.jboss.as.security.plugins.SecurityDomainContext; import org.jboss.as.undertow.Host; import org.jboss.as.undertow.ServletContainerService; import org.jboss.as.undertow.UndertowLogger; import org.jboss.as.undertow.UndertowMessages; import org.jboss.as.undertow.UndertowService; import org.jboss.as.undertow.security.AuditNotificationReceiver; import org.jboss.as.undertow.security.JAASIdentityManagerImpl; import org.jboss.as.undertow.session.DistributableSessionManager; import org.jboss.as.web.common.StartupContext; import org.jboss.as.web.common.WebInjectionContainer; import org.jboss.as.web.host.ContextActivator; import org.jboss.marshalling.ClassResolver; import org.jboss.marshalling.ModularClassResolver; import org.jboss.metadata.web.jboss.JBossWebMetaData; import org.jboss.modules.Module; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.StabilityMonitor; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; import org.jboss.msc.value.InjectedValue; import org.jboss.security.audit.AuditManager; /** * @author Stuart Douglas */ public class UndertowDeploymentService implements Service<UndertowDeploymentService> { private final DeploymentInfo deploymentInfo; private final InjectedValue<ServletContainerService> container = new InjectedValue<>(); private final WebInjectionContainer webInjectionContainer; private final Module module; private final JBossWebMetaData jBossWebMetaData; private final InjectedValue<SecurityDomainContext> securityDomainContextValue = new InjectedValue<SecurityDomainContext>(); private final InjectedValue<UndertowService> undertowService = new InjectedValue<>(); private final InjectedValue<DistributedCacheManagerFactory> distributedCacheManagerFactoryInjectedValue = new InjectedValue<DistributedCacheManagerFactory>(); private final InjectedValue<Host> host = new InjectedValue<>(); private volatile DeploymentManager deploymentManager; private volatile DistributableSessionManager<OutgoingDistributableSessionData> sessionManager; public UndertowDeploymentService(final DeploymentInfo deploymentInfo, final WebInjectionContainer webInjectionContainer, final Module module, final JBossWebMetaData jBossWebMetaData) { this.deploymentInfo = deploymentInfo; this.webInjectionContainer = webInjectionContainer; this.module = module; this.jBossWebMetaData = jBossWebMetaData; //todo: fix this if(jBossWebMetaData.getDistributable() != null) { deploymentInfo.addOuterHandlerChainWrapper(new HandlerWrapper() { @Override public HttpHandler wrap(final HttpHandler handler) { return sessionManager.wrapHandlers(handler, deploymentManager.getDeployment()); } }); } } @Override public void start(final StartContext startContext) throws StartException { if(jBossWebMetaData.getDistributable() != null) { String instanceId = undertowService.getValue().getInstanceId(); ClassResolver resolver = ModularClassResolver.getInstance(module.getModuleLoader()); sessionManager = new DistributableSessionManager<OutgoingDistributableSessionData>(this.distributedCacheManagerFactoryInjectedValue.getValue(), jBossWebMetaData, new ClassLoaderAwareClassResolver(resolver, module.getClassLoader()), deploymentInfo.getContextPath(), module.getClassLoader(), instanceId); deploymentInfo.setSessionManager(sessionManager); } //TODO Darren, check this! final List<ThreadSetupAction> setup = new ArrayList<ThreadSetupAction>(); setup.add(new ContextClassLoaderSetupAction(deploymentInfo.getClassLoader())); setup.addAll(deploymentInfo.getThreadSetupActions()); final CompositeThreadSetupAction threadSetupAction = new CompositeThreadSetupAction(setup); SecurityDomainContext sdc = securityDomainContextValue.getValue(); deploymentInfo.setIdentityManager(new JAASIdentityManagerImpl(sdc, jBossWebMetaData.getPrincipalVersusRolesMap(), threadSetupAction)); AuditManager auditManager = sdc.getAuditManager(); if (auditManager != null) { deploymentInfo.addNotificationReceiver(new AuditNotificationReceiver(auditManager)); } deploymentInfo.setConfidentialPortManager(getConfidentialPortManager()); StartupContext.setInjectionContainer(webInjectionContainer); try { deploymentManager = container.getValue().getServletContainer().addDeployment(deploymentInfo); deploymentManager.deploy(); try { HttpHandler handler = deploymentManager.start(); host.getValue().registerDeployment(deploymentInfo, handler); } catch (ServletException e) { throw new StartException(e); } } finally { StartupContext.setInjectionContainer(null); } } @Override public void stop(final StopContext stopContext) { try { deploymentManager.stop(); } catch (ServletException e) { throw new RuntimeException(e); } deploymentManager.undeploy(); deploymentInfo.setIdentityManager(null); sessionManager = null; host.getValue().unregisterDeployment(deploymentInfo); } @Override public UndertowDeploymentService getValue() throws IllegalStateException, IllegalArgumentException { return this; } public InjectedValue<ServletContainerService> getContainer() { return container; } public InjectedValue<Host> getHost() { return host; } public InjectedValue<SecurityDomainContext> getSecurityDomainContextValue() { return securityDomainContextValue; } public InjectedValue<UndertowService> getUndertowService() { return undertowService; } private ConfidentialPortManager getConfidentialPortManager() { return new ConfidentialPortManager() { @Override public int getConfidentialPort(HttpServerExchange exchange) { return container.getValue().lookupSecurePort("default"); } }; } public InjectedValue<DistributedCacheManagerFactory> getDistributedCacheManagerFactoryInjectedValue() { return distributedCacheManagerFactoryInjectedValue; } /** * Provides an API to start/stop the {@link UndertowDeploymentService}. * This should register/deregister the web context. */ protected static class ContextActivatorImpl implements ContextActivator { private final ServiceController<UndertowDeploymentService> controller; ContextActivatorImpl(ServiceController<UndertowDeploymentService> controller) { this.controller = controller; } /** * Provide access to the Servlet Context. */ /** * Start the web context asynchronously. * <p/> * This would happen during OSGi webapp deployment. * <p/> * No DUP can assume that all dependencies are available to make a blocking call * instead it should call this method. */ public synchronized void startAsync() { controller.setMode(ServiceController.Mode.ACTIVE); } /** * Start the web context synchronously. * <p/> * This would happen when the OSGi webapp gets explicitly started. */ public synchronized boolean start(long timeout, TimeUnit unit) throws TimeoutException { if (controller.getMode() == ServiceController.Mode.NEVER) { controller.setMode(ServiceController.Mode.ACTIVE); } final StabilityMonitor monitor = new StabilityMonitor(); monitor.addController(controller); try { if (!monitor.awaitStability(timeout, unit)) { throw UndertowMessages.MESSAGES.timeoutContextActivation(controller.getName()); } } catch (final InterruptedException e) { // ignore } finally { monitor.removeController(controller); } return true; } /** * Stop the web context synchronously. * <p/> * This would happen when the OSGi webapp gets explicitly stops. */ public synchronized boolean stop(long timeout, TimeUnit unit) { boolean result = true; if (controller.getMode() == ServiceController.Mode.ACTIVE) { controller.setMode(ServiceController.Mode.NEVER); } final StabilityMonitor monitor = new StabilityMonitor(); monitor.addController(controller); try { if (!monitor.awaitStability(timeout, unit)) { UndertowLogger.ROOT_LOGGER.debugf("Timeout stopping context: %s", controller.getName()); } } catch (final InterruptedException e) { // ignore } finally { monitor.removeController(controller); } return result; } @Override public ServletContext getServletContext() { //todo UndertowDeploymentService should be fully started before this method is called return controller.getValue().deploymentManager.getDeployment().getServletContext(); } } }