/******************************************************************************* * 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.deployer.core.internal; import java.io.File; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.virgo.kernel.artifact.ArtifactSpecification; import org.eclipse.virgo.kernel.artifact.plan.PlanDescriptor.Provisioning; import org.eclipse.virgo.nano.deployer.api.core.DeployerLogEvents; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.kernel.deployer.model.GCRoots; import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentity; import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer; import org.eclipse.virgo.kernel.install.artifact.InstallArtifact; import org.eclipse.virgo.kernel.install.artifact.InstallArtifactGraphInclosure; import org.eclipse.virgo.kernel.install.artifact.PlanInstallArtifact; import org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact; import org.eclipse.virgo.kernel.install.environment.InstallEnvironment; import org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer; import org.eclipse.virgo.nano.serviceability.NonNull; import org.eclipse.virgo.medic.eventlog.EventLogger; import org.eclipse.virgo.repository.Repository; import org.eclipse.virgo.repository.RepositoryAwareArtifactDescriptor; import org.eclipse.virgo.util.common.GraphNode; import org.eclipse.virgo.util.common.GraphNode.ExceptionThrowingDirectedAcyclicGraphVisitor; import org.eclipse.virgo.util.osgi.manifest.VersionRange; import org.osgi.framework.Version; /** * {@link PlanResolver} adds the immediate child nodes to a plan node. * <p /> * * <strong>Concurrent Semantics</strong><br /> * * This class is thread safe. * */ public class PlanResolver implements Transformer { private static final String PROVISIONING_PROPERTY_NAME = "org.eclipse.virgo.kernel.provisioning"; private static final String SCOPE_SEPARATOR = "-"; private final InstallArtifactGraphInclosure installArtifactGraphInclosure; private final GCRoots gcRoots; private final Repository repository; private final ArtifactIdentityDeterminer artifactIdentityDeterminer; private final EventLogger eventLogger; public PlanResolver(@NonNull InstallArtifactGraphInclosure installArtifactGraphInclosure, @NonNull GCRoots gcRoots, @NonNull Repository repository, @NonNull ArtifactIdentityDeterminer artifactIdentityDeterminer, @NonNull EventLogger eventLogger) { this.installArtifactGraphInclosure = installArtifactGraphInclosure; this.gcRoots = gcRoots; this.repository = repository; this.artifactIdentityDeterminer = artifactIdentityDeterminer; this.eventLogger = eventLogger; } /** * {@InheritDoc} */ @Override public void transform(GraphNode<InstallArtifact> installGraph, final InstallEnvironment installEnvironment) throws DeploymentException { installGraph.visit(new ExceptionThrowingDirectedAcyclicGraphVisitor<InstallArtifact, DeploymentException>() { @Override public boolean visit(GraphNode<InstallArtifact> graph) throws DeploymentException { PlanResolver.this.operate(graph.getValue()); return true; } }); } private void operate(InstallArtifact installArtifact) throws DeploymentException { if (installArtifact instanceof PlanInstallArtifact) { PlanInstallArtifact planInstallArtifact = (PlanInstallArtifact) installArtifact; if (planInstallArtifact.getGraph().getChildren().isEmpty()) { try { String scopeName = getArtifactScopeName(planInstallArtifact); GraphNode<InstallArtifact> graph = planInstallArtifact.getGraph(); List<ArtifactSpecification> artifactSpecifications = planInstallArtifact.getArtifactSpecifications(); for (ArtifactSpecification artifactSpecification : artifactSpecifications) { GraphNode<InstallArtifact> childInstallNode = obtainInstallArtifactGraph(artifactSpecification, scopeName, planInstallArtifact.getProvisioning()); boolean newNode = childInstallNode.getParents().isEmpty() && !(((AbstractInstallArtifact) childInstallNode.getValue()).getTopLevelDeployed()); graph.addChild(childInstallNode); if (newNode) { // Put child into the INSTALLING state as Transformers (like this) are after the // "begin install" // pipeline stage. InstallArtifact childInstallArtifact = childInstallNode.getValue(); ((AbstractInstallArtifact) childInstallArtifact).beginInstall(); } } } catch (DeploymentException de) { throw new DeploymentException("Deployment of " + planInstallArtifact + " failed: " + de.getMessage(), de); } } } } /** * Returns the scope name of the given {@link InstallArtifact} or <code>null</code> if the given InstallArtifact * does not belong to a scope. * * @param installArtifact the <code>InstallArtiface</code> whose scope name is required * @return the scope name or <code>null</code> if the given InstallArtifact does not belong to a scope */ private String getArtifactScopeName(InstallArtifact installArtifact) { if (installArtifact instanceof PlanInstallArtifact) { PlanInstallArtifact planInstallArtifact = (PlanInstallArtifact) installArtifact; boolean scoped = planInstallArtifact.isScoped(); if (scoped) { return planInstallArtifact.getName() + SCOPE_SEPARATOR + versionToShortString(planInstallArtifact.getVersion()); } } return installArtifact.getScopeName(); } private static String versionToShortString(Version version) { String result = version.toString(); while (result.endsWith(".0")) { result = result.substring(0, result.length() - 2); } return result; } private GraphNode<InstallArtifact> obtainInstallArtifactGraph(ArtifactSpecification artifactSpecification, String scopeName, Provisioning parentProvisioning) throws DeploymentException { GraphNode<InstallArtifact> sharedNode = null; ArtifactIdentity identity = null; File artifact = null; Map<String, String> properties = determineDeploymentProperties(artifactSpecification.getProperties(), parentProvisioning); String repositoryName = null; URI uri = artifactSpecification.getUri(); if (uri == null) { RepositoryAwareArtifactDescriptor repositoryAwareArtifactDescriptor = lookup(artifactSpecification); if (repositoryAwareArtifactDescriptor == null) { String type = artifactSpecification.getType(); String name = artifactSpecification.getName(); VersionRange versionRange = artifactSpecification.getVersionRange(); sharedNode = findSharedNode(type, name, versionRange, null); if (sharedNode == null) { this.eventLogger.log(DeployerLogEvents.ARTIFACT_NOT_FOUND, type, name, versionRange, this.repository.getName()); throw new DeploymentException(type + " '" + name + "' in version range '" + versionRange + "' not found"); } } else { URI artifactUri = repositoryAwareArtifactDescriptor.getUri(); artifact = new File(artifactUri); identity = new ArtifactIdentity(repositoryAwareArtifactDescriptor.getType(), repositoryAwareArtifactDescriptor.getName(), repositoryAwareArtifactDescriptor.getVersion(), scopeName); repositoryName = repositoryAwareArtifactDescriptor.getRepositoryName(); sharedNode = findSharedNode(identity); } } else { try { artifact = new File(uri); } catch (IllegalArgumentException e) { throw new DeploymentException("Invalid artifact specification URI '" + uri.toString() + "'", e); } identity = determineIdentity(uri, scopeName); sharedNode = findSharedNode(identity); } return sharedNode == null ? this.installArtifactGraphInclosure.constructGraphNode(identity, artifact, properties, repositoryName) : sharedNode; } private Map<String, String> determineDeploymentProperties(Map<String, String> properties, Provisioning parentProvisioning) { Map<String, String> deploymentProperties = new HashMap<String, String>(properties); deploymentProperties.put(PROVISIONING_PROPERTY_NAME, parentProvisioning.toString()); return deploymentProperties; } private RepositoryAwareArtifactDescriptor lookup(ArtifactSpecification specification) throws DeploymentException { String type = specification.getType(); String name = specification.getName(); VersionRange versionRange = specification.getVersionRange(); return this.repository.get(type, name, versionRange); } private ArtifactIdentity determineIdentity(URI artifactUri, String scopeName) throws DeploymentException { try { File artifact = new File(artifactUri); if (!artifact.exists()) { throw new DeploymentException(artifact + " does not exist"); } return determineIdentity(artifact, scopeName); } catch (Exception e) { throw new DeploymentException(e.getMessage() + ": uri='" + artifactUri + "'", e); } } private ArtifactIdentity determineIdentity(File file, String scopeName) throws DeploymentException { ArtifactIdentity artifactIdentity = this.artifactIdentityDeterminer.determineIdentity(file, scopeName); if (artifactIdentity == null) { this.eventLogger.log(DeployerLogEvents.INDETERMINATE_ARTIFACT_TYPE, file); throw new DeploymentException("Cannot determine the artifact identity of the file '" + file + "'"); } return artifactIdentity; } private GraphNode<InstallArtifact> findSharedNode(ArtifactIdentity artifactIdentity) { return ExistingNodeLocator.findSharedNode(this.gcRoots, artifactIdentity); } public GraphNode<InstallArtifact> findSharedNode(String type, String name, VersionRange versionRange, String scopeName) { return ExistingNodeLocator.findSharedNode(this.gcRoots, type, name, versionRange, scopeName); } }