/*******************************************************************************
* 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.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.virgo.kernel.artifact.ArtifactSpecification;
import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS;
import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry;
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.install.artifact.ArtifactIdentity;
import org.eclipse.virgo.kernel.install.artifact.ArtifactIdentityDeterminer;
import org.eclipse.virgo.kernel.install.artifact.ArtifactStorage;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifact;
import org.eclipse.virgo.kernel.install.artifact.InstallArtifactGraphFactory;
import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository;
import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ArtifactIdentityScoper;
import org.eclipse.virgo.kernel.install.artifact.internal.scoping.ScopeNameFactory;
import org.eclipse.virgo.nano.serviceability.NonNull;
import org.eclipse.virgo.nano.shim.scope.ScopeFactory;
import org.eclipse.virgo.medic.eventlog.EventLogger;
import org.eclipse.virgo.util.common.GraphNode;
import org.eclipse.virgo.util.math.OrderedPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link ParPlanInstallArtifact} is an {@link InstallArtifact} for a PAR file.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* This class is thread safe.
*
*/
final class ParPlanInstallArtifact extends StandardPlanInstallArtifact {
private static final ArrayList<ArtifactSpecification> EMPTY_ARTIFACT_SPECIFICATION_LIST = new ArrayList<ArtifactSpecification>();
private static final String META_INF_PATH = "//META-INF";
private final Object monitor = new Object();
private final InstallArtifactGraphFactory bundleInstallArtifactGraphFactory;
private final InstallArtifactGraphFactory configInstallArtifactGraphFactory;
private final ArtifactStorageFactory artifactStorageFactory;
private final ArtifactIdentityDeterminer artifactIdentityDeterminer;
private final List<GraphNode<InstallArtifact>> childInstallArtifacts;
private final InstallArtifactGraphFactory planInstallArtifactGraphFactory;
private static final Logger LOGGER = LoggerFactory.getLogger(ParPlanInstallArtifact.class);
public ParPlanInstallArtifact(@NonNull ArtifactIdentity identity, @NonNull ArtifactStorage artifactStorage,
@NonNull ArtifactStateMonitor artifactStateMonitor, @NonNull ScopeServiceRepository scopeServiceRepository,
@NonNull ScopeFactory scopeFactory, @NonNull EventLogger eventLogger, @NonNull InstallArtifactGraphFactory bundleInstallArtifactGraphFactory,
@NonNull InstallArtifactRefreshHandler refreshHandler, String repositoryName,
@NonNull InstallArtifactGraphFactory configInstallArtifactGraphFactory, @NonNull ArtifactStorageFactory artifactStorageFactory,
@NonNull ArtifactIdentityDeterminer artifactIdentityDeterminer, @NonNull InstallArtifactGraphFactory planInstallArtifactGraphFactory)
throws DeploymentException {
super(identity, true, true, Provisioning.AUTO, artifactStorage, artifactStateMonitor, scopeServiceRepository, scopeFactory, eventLogger,
refreshHandler, repositoryName, EMPTY_ARTIFACT_SPECIFICATION_LIST);
this.artifactStorageFactory = artifactStorageFactory;
this.configInstallArtifactGraphFactory = configInstallArtifactGraphFactory;
this.bundleInstallArtifactGraphFactory = bundleInstallArtifactGraphFactory;
this.planInstallArtifactGraphFactory = planInstallArtifactGraphFactory;
this.artifactIdentityDeterminer = artifactIdentityDeterminer;
List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts = findChildArtifacts(artifactStorage.getArtifactFS());
this.childInstallArtifacts = createChildInstallArtifacts(childArtifacts);
}
private List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> findChildArtifacts(ArtifactFS artifactFS) throws DeploymentException {
List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts = new ArrayList<OrderedPair<ArtifactIdentity, ArtifactFSEntry>>();
ArtifactFSEntry entry = artifactFS.getEntry("/");
ArtifactFSEntry[] children = entry.getChildren();
if (children.length == 0) {
throw new DeploymentException("Failed to find child artifacts in par " + artifactFS);
}
String scopeName = ScopeNameFactory.createScopeName(this.getName(), this.getVersion());
for (ArtifactFSEntry child : children) {
String name = child.getPath();
if (!META_INF_PATH.equals(name)) {
ArtifactIdentity artifactIdentity = this.artifactIdentityDeterminer.determineIdentity(child.getArtifactFS().getFile(), scopeName);
if (artifactIdentity != null) {
ArtifactIdentity scopedIdentity = ArtifactIdentityScoper.scopeArtifactIdentity(artifactIdentity);
childArtifacts.add(new OrderedPair<ArtifactIdentity, ArtifactFSEntry>(scopedIdentity, child));
} else {
LOGGER.warn("Skipping entry " + name + " as it is not of a recognized type");
}
}
}
return childArtifacts;
}
List<GraphNode<InstallArtifact>> createChildInstallArtifacts(List<OrderedPair<ArtifactIdentity, ArtifactFSEntry>> childArtifacts)
throws DeploymentException {
List<GraphNode<InstallArtifact>> childInstallArtifacts = new ArrayList<GraphNode<InstallArtifact>>();
for (OrderedPair<ArtifactIdentity, ArtifactFSEntry> childArtifact : childArtifacts) {
GraphNode<InstallArtifact> subGraph = null;
ArtifactIdentity identity = childArtifact.getFirst();
ArtifactFSEntry artifactFs = childArtifact.getSecond();
ArtifactStorage childArtifactStorage = createArtifactStorage(artifactFs, identity);
if (ArtifactIdentityDeterminer.BUNDLE_TYPE.equals(identity.getType())) {
subGraph = this.bundleInstallArtifactGraphFactory.constructInstallArtifactGraph(identity, childArtifactStorage, null, null);
} else if (ArtifactIdentityDeterminer.CONFIGURATION_TYPE.equals(identity.getType())) {
subGraph = this.configInstallArtifactGraphFactory.constructInstallArtifactGraph(identity, childArtifactStorage, null, null);
} else if (ArtifactIdentityDeterminer.PLAN_TYPE.equals(identity.getType())) {
subGraph = this.planInstallArtifactGraphFactory.constructInstallArtifactGraph(identity, childArtifactStorage, null, null);
}
if (subGraph == null) {
LOGGER.warn("Skipping " + identity + " as " + identity.getType() + " artifacts are not supported within a PAR");
} else {
childInstallArtifacts.add(subGraph);
}
}
return childInstallArtifacts;
}
/**
* {@inheritDoc}
*/
@Override
public void beginInstall() throws DeploymentException {
super.beginInstall();
List<GraphNode<InstallArtifact>> children;
synchronized (this.monitor) {
children = new ArrayList<GraphNode<InstallArtifact>>(this.childInstallArtifacts);
}
for (GraphNode<InstallArtifact> child : children) {
((AbstractInstallArtifact) child.getValue()).beginInstall();
}
}
/**
* {@inheritDoc}
*/
@Override
public void setGraph(GraphNode<InstallArtifact> graph) throws DeploymentException {
synchronized (this.monitor) {
super.setGraph(graph);
List<GraphNode<InstallArtifact>> children = graph.getChildren();
for (GraphNode<InstallArtifact> child : this.childInstallArtifacts) {
// Add any children that are not already present.
if (!isChildPresent(children, child)) {
graph.addChild(child);
}
}
}
}
private static boolean isChildPresent(List<GraphNode<InstallArtifact>> children, GraphNode<InstallArtifact> newChild) {
InstallArtifact newChildValue = newChild.getValue();
for (GraphNode<InstallArtifact> child : children) {
InstallArtifact childValue = child.getValue();
if (equalIdentities(childValue, newChildValue)) {
return true;
}
}
return false;
}
private static boolean equalIdentities(InstallArtifact ia1, InstallArtifact ia2) {
ArtifactIdentity id1 = ((AbstractInstallArtifact) ia1).getIdentity();
ArtifactIdentity id2 = ((AbstractInstallArtifact) ia2).getIdentity();
return id1.equals(id2);
}
private ArtifactStorage createArtifactStorage(ArtifactFSEntry artifactFSEntry, ArtifactIdentity artifactIdentity) {
ArtifactStorage innerStorage = this.artifactStorageFactory.create(artifactFSEntry.getArtifactFS().getFile(), artifactIdentity);
ArtifactStorage outerStorage = this.artifactStorage;
return new DelegatingArtifactStorage(innerStorage, outerStorage);
}
/**
* {@inheritDoc}
*/
@Override
public boolean refresh(String symbolicName) throws DeploymentException {
return super.refresh(symbolicName);
}
@Override
protected boolean doRefresh(String symbolicName) throws DeploymentException {
InstallArtifact childToRefresh = findChild(symbolicName);
if (childToRefresh == null) {
this.eventLogger.log(DeployerLogEvents.REFRESH_REQUEST_FAILED, symbolicName, getType(), getName(), getVersion());
throw new DeploymentException("Refresh failed: child '" + symbolicName + "' not found in " + getType() + "(" + getName() + ", "
+ getVersion() + ")");
}
return childToRefresh.refresh();
}
private InstallArtifact findChild(String symbolicName) {
InstallArtifact childToRefresh = null;
List<GraphNode<InstallArtifact>> children = getGraph().getChildren();
for (GraphNode<InstallArtifact> child : children) {
InstallArtifact childInstallArtifact = child.getValue();
String childName = childInstallArtifact.getName();
if (childName.equals(symbolicName) || childName.equals(ScopeNameFactory.createScopeName(getName(), getVersion()) + "-" + symbolicName)) {
childToRefresh = childInstallArtifact;
break;
}
}
return childToRefresh;
}
private static final class DelegatingArtifactStorage implements ArtifactStorage {
private final ArtifactStorage delegate;
private final ArtifactStorage sourceStorage;
private DelegatingArtifactStorage(ArtifactStorage delegate, ArtifactStorage sourceStorage) {
this.delegate = delegate;
this.sourceStorage = sourceStorage;
}
/**
* {@inheritDoc}
*/
public void delete() {
this.delegate.delete();
}
/**
* {@inheritDoc}
*/
public ArtifactFS getArtifactFS() {
return this.delegate.getArtifactFS();
}
/**
* {@inheritDoc}
*/
public void synchronize() {
this.sourceStorage.synchronize();
this.delegate.synchronize();
}
/**
* {@inheritDoc}
*/
public void synchronize(URI sourceUri) {
this.delegate.synchronize(sourceUri);
}
public void rollBack() {
this.delegate.rollBack();
this.sourceStorage.rollBack();
}
}
}