/* * JBoss, Home of Professional Open Source. * Copyright 2010, 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.server.deployment; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.jboss.as.server.logging.ServerLogger; import org.jboss.msc.service.AbstractServiceListener; import org.jboss.msc.service.DelegatingServiceRegistry; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceContainer; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceController.Mode; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartException; import org.jboss.msc.service.StopContext; import org.jboss.msc.value.InjectedValue; /** * A service which executes a particular phase of deployment. * * @param <T> the public type of this deployment unit phase * * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> */ final class DeploymentUnitPhaseService<T> implements Service<T> { private static final AttachmentKey<AttachmentList<DeploymentUnit>> UNVISITED_DEFERRED_MODULES = AttachmentKey.createList(DeploymentUnit.class); private final InjectedValue<DeployerChains> deployerChainsInjector = new InjectedValue<DeployerChains>(); private final DeploymentUnit deploymentUnit; private final Phase phase; private final AttachmentKey<T> valueKey; private final List<AttachedDependency> injectedAttachedDependencies = new ArrayList<AttachedDependency>(); /** * boolean value that tracks if this phase has already been run. * * If anything attempts to restart the phase a complete deployment restart is performed instead. */ private final AtomicBoolean runOnce = new AtomicBoolean(); private DeploymentUnitPhaseService(final DeploymentUnit deploymentUnit, final Phase phase, final AttachmentKey<T> valueKey) { this.deploymentUnit = deploymentUnit; this.phase = phase; this.valueKey = valueKey; } private static <T> DeploymentUnitPhaseService<T> create(final DeploymentUnit deploymentUnit, final Phase phase, AttachmentKey<T> valueKey) { return new DeploymentUnitPhaseService<T>(deploymentUnit, phase, valueKey); } static DeploymentUnitPhaseService<?> create(final DeploymentUnit deploymentUnit, final Phase phase) { return create(deploymentUnit, phase, phase.getPhaseKey()); } @SuppressWarnings("unchecked") public synchronized void start(final StartContext context) throws StartException { boolean allowRestart = restartAllowed(); if(runOnce.get() && !allowRestart) { ServerLogger.DEPLOYMENT_LOGGER.deploymentRestartDetected(deploymentUnit.getName()); //this only happens on deployment restart, which we don't support at the moment. //instead we are going to restart the complete deployment. //we get the deployment unit service name //add a listener to perform a restart when the service goes down //then stop the deployment unit service final ServiceName serviceName; if(deploymentUnit.getParent() == null) { serviceName = deploymentUnit.getServiceName(); } else { serviceName = deploymentUnit.getParent().getServiceName(); } ServiceController<?> controller = context.getController().getServiceContainer().getRequiredService(serviceName); controller.addListener(new AbstractServiceListener<Object>() { @Override public void transition(final ServiceController<?> controller, final ServiceController.Transition transition) { if(transition.getAfter().equals(ServiceController.Substate.DOWN)) { controller.setMode(Mode.ACTIVE); controller.removeListener(this); } } }); controller.setMode(Mode.NEVER); return; } runOnce.set(true); final DeployerChains chains = deployerChainsInjector.getValue(); final DeploymentUnit deploymentUnit = this.deploymentUnit; final List<RegisteredDeploymentUnitProcessor> list = chains.getChain(phase); final ListIterator<RegisteredDeploymentUnitProcessor> iterator = list.listIterator(); final ServiceContainer container = context.getController().getServiceContainer(); final ServiceTarget serviceTarget = context.getChildTarget().subTarget(); final String name = deploymentUnit.getName(); final DeploymentUnit parent = deploymentUnit.getParent(); final List<DeploymentUnitPhaseDependency> dependencies = new LinkedList<>(); final DeploymentPhaseContext processorContext = new DeploymentPhaseContextImpl(serviceTarget, new DelegatingServiceRegistry(container), dependencies, deploymentUnit, phase); // attach any injected values from the last phase for (AttachedDependency attachedDependency : injectedAttachedDependencies) { final Attachable target; if (attachedDependency.isDeploymentUnit()) { target = deploymentUnit; } else { target = processorContext; } if (attachedDependency.getAttachmentKey() instanceof ListAttachmentKey) { target.addToAttachmentList((AttachmentKey) attachedDependency.getAttachmentKey(), attachedDependency.getValue().getValue()); } else { target.putAttachment((AttachmentKey) attachedDependency.getAttachmentKey(), attachedDependency.getValue().getValue()); } } while (iterator.hasNext()) { final RegisteredDeploymentUnitProcessor processor = iterator.next(); try { if (shouldRun(deploymentUnit, processor)) { processor.getProcessor().deploy(processorContext); } } catch (Throwable e) { while (iterator.hasPrevious()) { final RegisteredDeploymentUnitProcessor prev = iterator.previous(); safeUndeploy(deploymentUnit, phase, prev); } throw ServerLogger.ROOT_LOGGER.deploymentPhaseFailed(phase, deploymentUnit, e); } } final Phase nextPhase = phase.next(); if (nextPhase != null) { final ServiceName serviceName = DeploymentUtils.getDeploymentUnitPhaseServiceName(deploymentUnit, nextPhase); final DeploymentUnitPhaseService<?> phaseService = DeploymentUnitPhaseService.create(deploymentUnit, nextPhase); final DeploymentUnitPhaseBuilder builder = processorContext.getAttachment(Attachments.DEPLOYMENT_UNIT_PHASE_BUILDER); final ServiceBuilder<?> phaseServiceBuilder = (builder != null) ? builder.build(serviceTarget, serviceName, phaseService) : serviceTarget.addService(serviceName, phaseService); for (DeploymentUnitPhaseDependency dependency: dependencies) { dependency.register(phaseServiceBuilder); } phaseServiceBuilder.addDependency(Services.JBOSS_DEPLOYMENT_CHAINS, DeployerChains.class, phaseService.getDeployerChainsInjector()); phaseServiceBuilder.addDependency(context.getController().getName()); final List<ServiceName> nextPhaseDeps = processorContext.getAttachment(Attachments.NEXT_PHASE_DEPS); if (nextPhaseDeps != null) { phaseServiceBuilder.addDependencies(nextPhaseDeps); } final List<AttachableDependency> nextPhaseAttachableDeps = processorContext.getAttachment(Attachments.NEXT_PHASE_ATTACHABLE_DEPS); if (nextPhaseAttachableDeps != null) { for (AttachableDependency attachableDep : nextPhaseAttachableDeps) { AttachedDependency result = new AttachedDependency(attachableDep.getAttachmentKey(), attachableDep.isDeploymentUnit()); phaseServiceBuilder.addDependency(attachableDep.getServiceName(), result.getValue()); phaseService.injectedAttachedDependencies.add(result); } } // Add a dependency on the parent's next phase if (parent != null) { phaseServiceBuilder.addDependencies(Services.deploymentUnitName(parent.getName(), nextPhase)); } // Make sure all sub deployments have finished this phase before moving to the next one List<DeploymentUnit> subDeployments = deploymentUnit.getAttachmentList(Attachments.SUB_DEPLOYMENTS); for (DeploymentUnit du : subDeployments) { phaseServiceBuilder.addDependencies(du.getServiceName().append(phase.name())); } // Defer the {@link Phase.FIRST_MODULE_USE} phase List<String> deferredModules = DeploymentUtils.getDeferredModules(deploymentUnit); if (nextPhase == Phase.FIRST_MODULE_USE) { Mode initialMode = getDeferableInitialMode(deploymentUnit, deferredModules); if (initialMode != Mode.ACTIVE) { ServerLogger.DEPLOYMENT_LOGGER.infoDeferDeploymentPhase(nextPhase, name, initialMode); phaseServiceBuilder.setInitialMode(initialMode); } } phaseServiceBuilder.install(); } } private Boolean restartAllowed() { final DeploymentUnit parent; if (deploymentUnit.getParent() == null) { parent = deploymentUnit; } else { parent = deploymentUnit.getParent(); } Boolean allowed = parent.getAttachment(Attachments.ALLOW_PHASE_RESTART); return allowed != null && allowed; } public synchronized void stop(final StopContext context) { final DeploymentUnit deploymentUnitContext = deploymentUnit; final DeployerChains chains = deployerChainsInjector.getValue(); final List<RegisteredDeploymentUnitProcessor> list = chains.getChain(phase); final ListIterator<RegisteredDeploymentUnitProcessor> iterator = list.listIterator(list.size()); while (iterator.hasPrevious()) { final RegisteredDeploymentUnitProcessor prev = iterator.previous(); safeUndeploy(deploymentUnitContext, phase, prev); } } private Mode getDeferableInitialMode(final DeploymentUnit deploymentUnit, List<String> deferredModules) { // Make the deferred module NEVER if (deferredModules.contains(deploymentUnit.getName())) { return Mode.NEVER; } Mode initialMode = Mode.ACTIVE; DeploymentUnit parent = DeploymentUtils.getTopDeploymentUnit(deploymentUnit); if (parent == deploymentUnit) { List<DeploymentUnit> subDeployments = parent.getAttachmentList(Attachments.SUB_DEPLOYMENTS); for (DeploymentUnit du : subDeployments) { // Always make the EAR LAZY if it could contain deferrable sub-deployments if (du.hasAttachment(Attachments.OSGI_MANIFEST)) { initialMode = Mode.LAZY; break; } } // Initialize the list of unvisited deferred modules if (initialMode == Mode.LAZY) { for (DeploymentUnit du : subDeployments) { parent.addToAttachmentList(UNVISITED_DEFERRED_MODULES, du); } } } else { // Make the non-deferred sibling PASSIVE if it is not the last to visit List<DeploymentUnit> unvisited = parent.getAttachmentList(UNVISITED_DEFERRED_MODULES); synchronized (unvisited) { unvisited.remove(deploymentUnit); if (!deferredModules.isEmpty() || !unvisited.isEmpty()) { initialMode = Mode.PASSIVE; } } } return initialMode; } private static void safeUndeploy(final DeploymentUnit deploymentUnit, final Phase phase, final RegisteredDeploymentUnitProcessor prev) { try { if (shouldRun(deploymentUnit, prev)) { prev.getProcessor().undeploy(deploymentUnit); } } catch (Throwable t) { ServerLogger.DEPLOYMENT_LOGGER.caughtExceptionUndeploying(t, prev.getProcessor(), phase, deploymentUnit); } } public synchronized T getValue() throws IllegalStateException, IllegalArgumentException { return deploymentUnit.getAttachment(valueKey); } InjectedValue<DeployerChains> getDeployerChainsInjector() { return deployerChainsInjector; } private static boolean shouldRun(final DeploymentUnit unit, final RegisteredDeploymentUnitProcessor deployer) { Set<String> shouldNotRun = unit.getAttachment(Attachments.EXCLUDED_SUBSYSTEMS); if (shouldNotRun == null) { if (unit.getParent() != null) { shouldNotRun = unit.getParent().getAttachment(Attachments.EXCLUDED_SUBSYSTEMS); } if (shouldNotRun == null) { return true; } } return !shouldNotRun.contains(deployer.getSubsystemName()); } }