/*******************************************************************************
* 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.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.tree.TreeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.virgo.nano.deployer.api.core.DeploymentException;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListenerSupport;
import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact;
import org.eclipse.virgo.util.common.GraphNode;
/**
* <code>AtomicInstallArtifactLifecycleListener</code> is an InstallArtifactLifecycleListener which initiates state
* changes on an atomic ancestor of an artifact when the artifact changes state.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
* Thread-safe
*
*/
final class AtomicInstallArtifactLifecycleListener extends InstallArtifactLifecycleListenerSupport {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* {@inheritDoc}
*/
@Override
public void onStarting(InstallArtifact installArtifact) throws DeploymentException {
logger.debug("Processing atomic starting event for {}", installArtifact);
for (InstallArtifact atomicParent : getAtomicParents(installArtifact)) {
if (aChildIsRefreshing(atomicParent)) {
logger.info("Atomic starting event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
} else {
logger.info("Propagating atomic starting event from {} to {}", installArtifact, atomicParent);
atomicParent.start();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onStartFailed(InstallArtifact installArtifact, Throwable cause) throws DeploymentException {
logger.debug("Processing atomic start failed (stop) event for {}", installArtifact);
for (InstallArtifact atomicParent : getAtomicParents(installArtifact)) {
if (aChildIsRefreshing(atomicParent)) {
logger.info("Atomic start failed event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
} else {
logger.info("Propagating atomic start failed (stop) event from {} to {}", installArtifact, atomicParent);
atomicParent.stop();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onStopped(InstallArtifact installArtifact) {
logger.debug("Processing atomic stopped event for {}", installArtifact);
for (InstallArtifact atomicParent : getAtomicParents(installArtifact)) {
if (aChildIsRefreshing(atomicParent)) {
logger.info("Atomic stopped event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
} else {
logger.info("Propagating atomic stopped event from {} to {}", installArtifact, atomicParent);
try {
atomicParent.stop();
} catch (DeploymentException e) {
logger.warn("Unable to propagate stopped event to an atomic root due to an exception", e);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onUninstalled(InstallArtifact installArtifact) throws DeploymentException {
logger.debug("Processing atomic uninstalled event for {}", installArtifact);
for (InstallArtifact atomicParent : getAtomicParents(installArtifact)) {
if (aChildIsRefreshing(atomicParent)) {
logger.info("Atomic uninstalled event not propagated from {} as a child of {} is refreshing.", installArtifact, atomicParent);
} else {
logger.info("Propagating atomic uninstalled event from {} to {}", installArtifact, atomicParent);
atomicParent.uninstall();
}
}
}
private Set<InstallArtifact> getAtomicParents(InstallArtifact installArtifact) {
Set<InstallArtifact> atomicParents = new HashSet<InstallArtifact>();
for (InstallArtifact parent : getParentInstallArtifacts(installArtifact)) {
if (isAtomicInstallArtifact(parent)) {
atomicParents.add(parent);
}
}
return atomicParents;
}
/**
* Return true if this is an {@link InstallArtifact} and contains others (in the {@link TreeNode}) and is an atomic
* container.
*
* @param installArtifact
* @return true iff this is an artifact that has the {@link PlanInstallArtifact#isAtomic} attribute equal to true
*/
private static final boolean isAtomicInstallArtifact(InstallArtifact installArtifact) {
if (installArtifact instanceof PlanInstallArtifact) {
return ((PlanInstallArtifact) installArtifact).isAtomic();
}
return false;
}
/**
* Get the parent {@link InstallArtifact}s in the {@link GraphNode} associated with the {@link InstallArtifact}, if
* there are any.
*
* @param installArtifact to find the parents of
* @return the parent artifacts in the graph, never <code>null</code>
*/
private static final Set<InstallArtifact> getParentInstallArtifacts(InstallArtifact installArtifact) {
Set<InstallArtifact> parentInstallArtifacts = new HashSet<InstallArtifact>();
GraphNode<InstallArtifact> iaGraph = installArtifact.getGraph();
if (iaGraph != null) {
List<GraphNode<InstallArtifact>> parents = iaGraph.getParents();
for (GraphNode<InstallArtifact> parent : parents) {
parentInstallArtifacts.add(parent.getValue());
}
}
return parentInstallArtifacts;
}
/**
* Determines if <em>any</em> child of this {@link InstallArtifact} has
* {@link AbstractInstallArtifact#isRefreshing() isRefreshing()} which returns true.
*
* @param atomicParent whose children are checked
* @return true if any child is refreshing, otherwise false.
*/
private static boolean aChildIsRefreshing(InstallArtifact atomicParent) {
for (InstallArtifact child : childrenOf(atomicParent)) {
if (child instanceof AbstractInstallArtifact) {
AbstractInstallArtifact aChild = (AbstractInstallArtifact) child;
if (aChild.isRefreshing()) {
return true;
}
}
}
return false;
}
/**
* @param parent
* @return an array of the children of the parent (which can be zero length)
*/
private static InstallArtifact[] childrenOf(InstallArtifact parent) {
List<InstallArtifact> children = new ArrayList<InstallArtifact>();
if (parent != null) {
GraphNode<InstallArtifact> graph = parent.getGraph();
if (graph != null) {
for (GraphNode<InstallArtifact> childBranch : graph.getChildren()) {
InstallArtifact child = childBranch.getValue();
if (child != null) {
children.add(child);
}
}
}
}
return children.toArray(new InstallArtifact[children.size()]);
}
}