/******************************************************************************* * Copyright (c) 2008, 2011 VMware Inc. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VMware Inc. - initial contribution * EclipseSource - Bug 358442 Change InstallArtifact graph from a tree to a DAG *******************************************************************************/ package org.eclipse.virgo.kernel.install.artifact.internal; import java.util.ArrayList; import java.util.List; import org.eclipse.virgo.kernel.artifact.ArtifactSpecification; import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor.Provisioning; import org.eclipse.virgo.nano.core.AbortableSignal; import org.eclipse.virgo.nano.deployer.api.core.DeployerLogEvents; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.kernel.deployer.core.internal.AbortableSignalJunction; import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity; import org.eclipse.virgo.kernel.install.artifact.ArtifactStorage; import org.eclipse.virgo.kernel.install.artifact.InstallArtifact; import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact; import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository; import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper; import org.eclipse.virgo.nano.serviceability.NonNull; import org.eclipse.virgo.nano.shim.scope.Scope; import org.eclipse.virgo.nano.shim.scope.ScopeFactory; import org.eclipse.virgo.medic.eventlog.EventLogger; import org.eclipse.virgo.util.common.GraphNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link StandardPlanInstallArtifact} is the standard implementation of {@link PlanInstallArtifact}. * <p /> * * <strong>Concurrent Semantics</strong><br /> * * This class is thread safe. * */ public class StandardPlanInstallArtifact extends AbstractInstallArtifact implements PlanInstallArtifact { private static final Logger LOGGER = LoggerFactory.getLogger(StandardPlanInstallArtifact.class); private final Object monitor = new Object(); private final ScopeServiceRepository scopeServiceRepository; private final ScopeFactory scopeFactory; private final InstallArtifactRefreshHandler refreshHandler; private final boolean atomic; private final boolean scoped; private final Provisioning provisioning; private final List<ArtifactSpecification> artifactSpecifications; protected final EventLogger eventLogger; private Scope applicationScope; protected StandardPlanInstallArtifact(@NonNull ArtifactIdentity artifactIdentity, boolean atomic, boolean scoped, @NonNull Provisioning provisioning, @NonNull ArtifactStorage artifactStorage, @NonNull ArtifactStateMonitor artifactStateMonitor, @NonNull ScopeServiceRepository scopeServiceRepository, @NonNull ScopeFactory scopeFactory, @NonNull EventLogger eventLogger, @NonNull InstallArtifactRefreshHandler refreshHandler, String repositoryName, List<ArtifactSpecification> artifactSpecifications) throws DeploymentException { super(artifactIdentity, artifactStorage, artifactStateMonitor, repositoryName, eventLogger); policeNestedScopes(artifactIdentity, scoped, eventLogger); this.scopeServiceRepository = scopeServiceRepository; this.scopeFactory = scopeFactory; this.eventLogger = eventLogger; this.refreshHandler = refreshHandler; this.atomic = atomic; this.scoped = scoped; if (provisioning == Provisioning.INHERIT) { throw new IllegalArgumentException("A plan's provisoning behaviour may not be set to INHERIT"); } this.provisioning = provisioning; this.artifactSpecifications = artifactSpecifications; } private void policeNestedScopes(ArtifactIdentity artifactIdentity, boolean scoped, EventLogger eventLogger) throws DeploymentException { if (artifactIdentity.getScopeName() != null && scoped) { eventLogger.log(DeployerLogEvents.NESTED_SCOPES_NOT_SUPPORTED, artifactIdentity.getType(), ArtifactIdentityScoper.getUnscopedName(artifactIdentity), artifactIdentity.getVersion(), artifactIdentity.getScopeName()); throw new DeploymentException("Nested scope detected", true); } } protected final List<GraphNode<InstallArtifact>> getChildrenSnapshot() { List<GraphNode<InstallArtifact>> children = new ArrayList<GraphNode<InstallArtifact>>(); synchronized (this.monitor) { children.addAll(getGraph().getChildren()); } return children; } /** * {@inheritDoc} */ @Override protected final void doStart(AbortableSignal signal) throws DeploymentException { List<GraphNode<InstallArtifact>> children = getChildrenSnapshot(); int numChildren = children.size(); // The SignalJunction constructor will drive the signal if numChildren == 0. AbortableSignalJunction signalJunction = new AbortableSignalJunction(signal, numChildren); LOGGER.debug("Created {} that will notify {} to track start of {}", new Object[] { signalJunction, signal, this }); List<AbortableSignal> subSignals = signalJunction.getSignals(); for (int childIndex = 0; childIndex < numChildren && !signalJunction.failed(); childIndex++) { InstallArtifact childArtifact = children.get(childIndex).getValue(); AbortableSignal subSignal = subSignals.get(childIndex); LOGGER.debug("Starting {} with signal {} from {}", new Object[] { childArtifact, subSignal, signalJunction }); childArtifact.start(subSignal); } } /** * {@inheritDoc} */ @Override protected final void doStop() throws DeploymentException { DeploymentException firstFailure = null; for (GraphNode<InstallArtifact> child : getChildrenSnapshot()) { try { child.getValue().stop(); } catch (DeploymentException e) { firstFailure = e; } } if (firstFailure != null) { throw firstFailure; } } /** * {@inheritDoc} */ @Override protected final void doUninstall() throws DeploymentException { deScope(); // TODO this was placed here in copying from the old deployer, but it is insufficient. Need to // consider stop/start/stop etc. for package and service scoping. DeploymentException firstFailure = null; for (GraphNode<InstallArtifact> child : getChildrenSnapshot()) { getGraph().removeChild(child); // Avoid uninstalling shared child if (!inUse(child)) { try { child.getValue().uninstall(); } catch (DeploymentException e) { firstFailure = e; } } } if (firstFailure != null) { throw firstFailure; } } private boolean inUse(GraphNode<InstallArtifact> child) { AbstractInstallArtifact installArtifact = (AbstractInstallArtifact) child.getValue(); boolean isTopLevelDeployed = installArtifact.getTopLevelDeployed(); return isTopLevelDeployed || !child.getParents().isEmpty(); } public void scope() throws DeploymentException { if (isScoped()) { List<InstallArtifact> scopeMembers = new PlanMemberCollector().collectPlanMembers(this); PlanScoper planScoper = new PlanScoper(scopeMembers, getName(), getVersion(), this.scopeServiceRepository, this.eventLogger); String scopeName = planScoper.getScopeName(); synchronized (this.monitor) { this.applicationScope = this.scopeFactory.getApplicationScope(scopeName); // TODO Do we really need to hold this lock while we're driving the planScoper? planScoper.scope(); } } } private void deScope() { synchronized (this.monitor) { if (this.applicationScope != null) { this.scopeFactory.destroyApplicationScope(this.applicationScope); this.scopeServiceRepository.clearScope(this.applicationScope.getScopeName()); this.applicationScope = null; } } } /** * {@inheritDoc} */ public boolean refreshScope() { return this.refreshHandler.refresh(this); } @Override public boolean refresh() throws DeploymentException { this.eventLogger.log(DeployerLogEvents.INSTALL_ARTIFACT_REFRESH_NOT_SUPPORTED, getType(), getName(), getVersion(), getType()); return false; } public final boolean isAtomic() { return this.atomic; } public final boolean isScoped() { return this.scoped; } /** * {@inheritDoc} */ public Provisioning getProvisioning() { return this.provisioning; } public final List<ArtifactSpecification> getArtifactSpecifications() { return this.artifactSpecifications; } }