All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.bdeploy.bhive.cli.TreeTool Maven / Gradle / Ivy

Go to download

Public API including dependencies, ready to be used for integrations and plugins.

There is a newer version: 7.3.6
Show newest version
package io.bdeploy.bhive.cli;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Predicate;
import java.util.stream.IntStream;

import io.bdeploy.bhive.BHive;
import io.bdeploy.bhive.cli.TreeTool.TreeConfig;
import io.bdeploy.bhive.model.Manifest;
import io.bdeploy.bhive.model.ObjectId;
import io.bdeploy.bhive.model.Tree;
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.TreeDiff;
import io.bdeploy.bhive.objects.view.scanner.TreeElementDiff;
import io.bdeploy.bhive.objects.view.scanner.TreeElementDiff.DifferenceType;
import io.bdeploy.bhive.objects.view.scanner.TreeVisitor;
import io.bdeploy.bhive.op.ObjectSizeOperation;
import io.bdeploy.bhive.op.ScanOperation;
import io.bdeploy.common.cfg.Configuration.EnvironmentFallback;
import io.bdeploy.common.cfg.Configuration.Help;
import io.bdeploy.common.cfg.Configuration.Validator;
import io.bdeploy.common.cfg.ExistingPathValidator;
import io.bdeploy.common.cfg.PathOwnershipValidator;
import io.bdeploy.common.cli.ToolBase.CliTool.CliName;
import io.bdeploy.common.cli.ToolBase.ConfiguredCliTool;
import io.bdeploy.common.cli.ToolCategory;
import io.bdeploy.common.cli.data.DataResult;
import io.bdeploy.common.cli.data.RenderableResult;
import io.bdeploy.common.util.FormatHelper;

/**
 * A tool to list and diff {@link Manifest} {@link Tree}s.
 */
@Help("Query and diff manifest root trees in the given BHive")
@ToolCategory(BHiveCli.MAINTENANCE_TOOLS)
@CliName("tree")
public class TreeTool extends ConfiguredCliTool {

    private static final String EMPTY_ID = "0000000000000000000000000000000000000000";

    public @interface TreeConfig {

        @Help("The BHive to use. Alternatively use --remote.")
        @EnvironmentFallback("BHIVE")
        @Validator({ ExistingPathValidator.class, PathOwnershipValidator.class })
        String hive();

        @Help("List content of given manifest")
        String list();

        @Help("Give two manifests to diff.")
        String[] diff() default {};
    }

    public TreeTool() {
        super(TreeConfig.class);
    }

    @Override
    protected RenderableResult run(TreeConfig config) {
        helpAndFailIfMissing(config.hive(), "Missing --hive");

        Path path = Paths.get(config.hive());
        try (BHive hive = new BHive(path.toUri(), getAuditorFactory().apply(path), getActivityReporter())) {
            if (config.list() != null) {
                TreeView snap = hive.execute(new ScanOperation().setManifest(Manifest.Key.parse(config.list())));
                format(snap);
                return createSuccess();
            } else if (config.diff().length > 0) {
                if (config.diff().length != 2) {
                    helpAndFail("Currently only support diff of two manifests");
                }
                TreeView t1 = hive.execute(new ScanOperation().setManifest(Manifest.Key.parse(config.diff()[0])));
                TreeView t2 = hive.execute(new ScanOperation().setManifest(Manifest.Key.parse(config.diff()[1])));

                return format(new TreeDiff(t1, t2).diff(), t1, hive);
            } else {
                return createNoOp();
            }
        }
    }

    private DataResult format(List diff, TreeView original, BHive hive) {
        for (TreeElementDiff ted : diff) {
            switch (ted.getType()) {
                case CONTENT_DIFF:
                    out().println("C: [" + ted.getLeftType().name().charAt(0) + "]" + ted.getLeft().getElementId() + " : ["
                            + ted.getRightType().name().charAt(0) + "]" + ted.getRight().getElementId() + " "
                            + ted.getLeft().getPathString());
                    break;
                case ONLY_LEFT:
                    out().println("L: [" + ted.getLeftType().name().charAt(0) + "]" + ted.getLeft().getElementId() + " : [ ]"
                            + EMPTY_ID + " " + ted.getLeft().getPathString());
                    break;
                case ONLY_RIGHT:
                    out().println("R: [ ]" + EMPTY_ID + " : [" + ted.getRightType().name().charAt(0) + "]"
                            + ted.getRight().getElementId() + " " + ted.getRight().getPathString());
                    break;
            }
        }

        // count size difference when updating to right - collect all only right or diff, but exclude objects with existing hash
        SortedSet existingObjs = new TreeSet<>();
        Predicate enlist = ev -> {
            existingObjs.add(ev.getElementId());
            return true;
        };
        original.visit(new TreeVisitor.Builder().onBlob(enlist::test).onTree(enlist::test).onManifestRef(enlist::test).build());

        ObjectSizeOperation oso = new ObjectSizeOperation();
        diff.stream().filter(d -> d.getType() != DifferenceType.ONLY_LEFT).map(d -> d.getRight().getElementId())
                .filter(o -> !existingObjs.contains(o)).forEach(oso::addObject);

        Long size = hive.execute(oso);

        return createSuccess().addField("Difference Size", FormatHelper.formatFileSize(size));
    }

    private void format(TreeView snap) {
        snap.visit(new TreeVisitor.Builder().onBlob(b -> {
            indent(b);
            out().println("[B] " + verbose(b));
        }).onTree(t -> {
            indent(t);
            out().println("[T] " + verbose(t)
                    + ((t instanceof ManifestRefView) ? (" {R:" + ((ManifestRefView) t).getReferenced() + "}") : ""));
            return true;
        }).onMissing(m -> {
            indent(m);
            out().println("[X] " + verbose(m) + " {MISSING}");
        }).build());
    }

    private String verbose(ElementView x) {
        StringBuilder builder = new StringBuilder();
        builder.append(x.getName());
        if (isVerbose()) {
            IntStream.range(0, Math.max(1, 80 - (x.getPath().size() * 2) - x.getName().length()))
                    .forEach(i -> builder.append(' '));
            builder.append(x.getElementId());
        }
        return builder.toString();
    }

    private void indent(ElementView e) {
        e.getPath().forEach(x -> out().print("  "));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy