io.bdeploy.bhive.op.ObjectListOperation Maven / Gradle / Ivy
Show all versions of api Show documentation
package io.bdeploy.bhive.op;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bdeploy.bhive.BHive;
import io.bdeploy.bhive.ReadOnlyOperation;
import io.bdeploy.bhive.audit.AuditParameterExtractor.AuditStrategy;
import io.bdeploy.bhive.audit.AuditParameterExtractor.AuditWith;
import io.bdeploy.bhive.model.Manifest;
import io.bdeploy.bhive.model.ObjectId;
import io.bdeploy.bhive.model.Tree;
import io.bdeploy.bhive.objects.view.BlobView;
import io.bdeploy.bhive.objects.view.ElementView;
import io.bdeploy.bhive.objects.view.ManifestRefView;
import io.bdeploy.bhive.objects.view.TreeView;
import io.bdeploy.bhive.objects.view.scanner.TreeVisitor;
import io.bdeploy.common.ActivityReporter.Activity;
/**
* List {@link ObjectId}s available in the {@link BHive}.
*
* Typically used to retrieve a flattened list of {@link ObjectId}s recursively
* required by one or more {@link Manifest}s.
*
* If no {@link Manifest} or {@linkplain ObjectId tree} is set, an exception is thrown.
*/
@ReadOnlyOperation
public class ObjectListOperation extends BHive.Operation> {
private static final Logger log = LoggerFactory.getLogger(ObjectListOperation.class);
@AuditWith(AuditStrategy.COLLECTION_PEEK)
private final Set manifests = new LinkedHashSet<>();
@AuditWith(AuditStrategy.COLLECTION_SIZE)
private final Set trees = new LinkedHashSet<>();
@AuditWith(AuditStrategy.COLLECTION_SIZE)
private final Set treeExcludes = new LinkedHashSet<>();
private boolean ignoreMissingManifest = false;
@Override
public Set call() throws Exception {
try (Activity activity = getActivityReporter().start("Finding Objects", -1)) {
if (manifests.isEmpty() && trees.isEmpty()) {
throw new IllegalStateException("At least one manifest or root tree must be set.");
}
// Load all trees that are referenced by the manifest
for (Manifest.Key m : manifests) {
try {
trees.add(execute(new ManifestLoadOperation().setManifest(m)).getRoot());
} catch (Exception e) {
if (!Boolean.TRUE.equals(execute(new ManifestExistsOperation().setManifest(m)) && ignoreMissingManifest)) {
// no longer exists, and we want to ignore this - just go on.
continue;
}
throw new IllegalStateException("Cannot read manifest even though it exists", e);
}
}
// Collect all (sub)-trees based on he provided root trees
// The result is ordered: First the parent tree then it's children
Map> object2Tree = new LinkedHashMap<>();
for (ObjectId tree : trees) {
TreeView treeView = execute(new ScanOperation().setTree(tree));
if (treeView.getElementId() == null) {
log.warn("Skipping damaged tree: {}", tree);
continue;
}
treeView.visit(new TreeVisitor.Builder().onTree(t -> {
// Check if we already have that tree or if it is excluded
ObjectId treeId = t.getElementId();
if (object2Tree.containsKey(treeId) || treeExcludes.contains(treeId)) {
return false;
}
// Collect and continue
object2Tree.put(treeId, flattenTree(t));
return true;
}).build());
activity.workAndCancelIfRequested(1);
}
// Reverse the list so that sub-trees are first
List> allTrees = new ArrayList<>(object2Tree.values());
Collections.reverse(allTrees);
// Collect all objects referring to the tree
// First we add the children then we add the parent tree
// We intentionally do not use a visitor here as we just need the direct children
Set result = new LinkedHashSet<>();
for (List flatTree : allTrees) {
result.addAll(flatTree);
}
return result;
}
}
private List flattenTree(TreeView tv) {
List result = new ArrayList<>();
for (ElementView child : tv.getChildren().values()) {
if (child instanceof BlobView) {
result.add(child.getElementId());
} else if (child instanceof ManifestRefView) {
ManifestRefView refView = (ManifestRefView) child;
result.add(refView.getReferenceId());
}
}
result.add(tv.getElementId());
return result;
}
/**
* Restrict {@link ObjectId} listing to the given {@link Manifest}(s).
*/
public ObjectListOperation addManifest(Collection manifests) {
this.manifests.addAll(manifests);
return this;
}
/**
* Restrict {@link ObjectId} listing to the given {@link Tree}(s).
*/
public ObjectListOperation addTree(Set trees) {
this.trees.addAll(trees);
return this;
}
/**
* When scanning, exclude the given {@link Tree} and all of it's children.
*/
public ObjectListOperation excludeTree(Set trees) {
this.treeExcludes.addAll(trees);
return this;
}
/**
* Restrict {@link ObjectId} listing to the given {@link Manifest}(s).
*/
public ObjectListOperation addManifest(Manifest.Key manifest) {
manifests.add(manifest);
return this;
}
/**
* Restrict {@link ObjectId} listing to the given {@link Tree}(s).
*/
public ObjectListOperation addTree(ObjectId tree) {
trees.add(tree);
return this;
}
/**
* When scanning, exclude the given {@link Tree} and all of it's children.
*/
public ObjectListOperation excludeTree(ObjectId tree) {
treeExcludes.add(tree);
return this;
}
/**
* Whether to ignore if a manifest is no longer present for listing.
*
* Only if the manifest is not there anymore this fact will be ignored. If the manifest
* cannot be loaded for another reason, this still throws an Exception.
*/
public ObjectListOperation ignoreMissingManifest(boolean ignore) {
this.ignoreMissingManifest = ignore;
return this;
}
}