com.hazelcast.hzblame.blame.Blame.java Source code

Java tutorial

Introduction

Here is the source code for com.hazelcast.hzblame.blame.Blame.java

Source

/*
 * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.hzblame.blame;

import com.hazelcast.common.AbstractGitClass;
import com.hazelcast.hzblame.utils.CommandLineOptions;
import com.hazelcast.utils.BufferingOutputHandler;
import com.hazelcast.utils.PropertyReader;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.eclipse.jgit.api.Git;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hazelcast.utils.CsvUtils.NOT_AVAILABLE;
import static com.hazelcast.utils.CsvUtils.readCSV;
import static com.hazelcast.utils.DebugUtils.debug;
import static com.hazelcast.utils.DebugUtils.isDebug;
import static com.hazelcast.utils.DebugUtils.print;
import static com.hazelcast.utils.DebugUtils.printGreen;
import static com.hazelcast.utils.DebugUtils.printRed;
import static com.hazelcast.utils.DebugUtils.printYellow;
import static com.hazelcast.utils.GitUtils.asString;
import static com.hazelcast.utils.GitUtils.checkout;
import static com.hazelcast.utils.GitUtils.compile;
import static com.hazelcast.utils.GitUtils.getCommit;
import static com.hazelcast.utils.GitUtils.getFirstParent;
import static org.eclipse.jgit.lib.Constants.HEAD;

public class Blame extends AbstractGitClass {

    private static final int MAX_EE_COMMIT_SKIPS = 10;

    private final Path commitPath = Paths.get("ee-os.csv");
    private final Map<String, String> commits = new HashMap<>();

    private final BufferingOutputHandler outputHandler;
    private final Invoker invoker;

    private final CommandLineOptions commandLineOptions;

    private final boolean isDry;
    private final boolean isEE;
    private final int limit;
    private final int retriesOnTestSuccess;

    private String currentNameOS;
    private String currentNameEE;

    private int counter;

    public Blame(PropertyReader propertyReader, CommandLineOptions commandLineOptions) {
        super("blame", propertyReader);
        this.commandLineOptions = commandLineOptions;

        this.isDry = commandLineOptions.isDry();
        this.isEE = commandLineOptions.isEE();
        this.limit = commandLineOptions.getLimit();
        this.retriesOnTestSuccess = commandLineOptions.getRetriesOnTestSuccess();

        this.outputHandler = new BufferingOutputHandler();
        this.invoker = new DefaultInvoker().setOutputHandler(outputHandler)
                .setMavenHome(new File("/usr/share/maven"));
    }

    @Override
    public void doRun() throws Exception {
        File projectRoot = getProjectRoot();
        List<String> goals = getMavenGoals();
        debug("Maven goals: %s", goals);
        if (isEE) {
            readCSV(commitPath, commits);
        }

        while (setNextCommit()) {
            // compile version
            checkout(branchName, gitOS, currentCommitOS);
            compile(invoker, outputHandler, gitOS, currentCommitOS, isDry, false);
            if (isEE) {
                checkout(branchName, gitEE, currentCommitEE);
                compile(invoker, outputHandler, gitEE, currentCommitEE, isDry, true);
            }
            // execute test
            int successCount = 0;
            for (int retryCount = 1; retryCount <= retriesOnTestSuccess; retryCount++) {
                if (!executeTest(projectRoot, goals, retryCount)) {
                    break;
                }
                successCount++;
            }
            if (successCount == retriesOnTestSuccess) {
                printGreen("Test passed without errors!");
                break;
            }
            System.out.println();
        }
    }

    private File getProjectRoot() {
        Git git = isEE ? gitEE : gitOS;
        return git.getRepository().getDirectory().getParentFile();
    }

    private List<String> getMavenGoals() {
        String testModule = isEE ? "hazelcast-enterprise" : "hazelcast";
        if (commandLineOptions.hasTestModule()) {
            testModule = commandLineOptions.getTestModule();
        }
        String testMethod = commandLineOptions.hasTestMethod() ? "#" + commandLineOptions.getTestMethod() : "";

        List<String> goals = new LinkedList<>();
        goals.add("test");
        if (commandLineOptions.hasMavenProfile()) {
            goals.add("-P" + commandLineOptions.getMavenProfile());
        }
        goals.add("-pl " + testModule);
        goals.add("-Dtest=" + commandLineOptions.getTestClass() + testMethod);
        return goals;
    }

    private boolean setNextCommit() throws IOException {
        if (counter++ >= limit && limit > 0) {
            printGreen("Done!");
            return false;
        }
        if (counter == 1) {
            return setFirstCommit();
        }
        switch (commandLineOptions.getSearchMode()) {
        case LINEAR:
            return setNextCommitLinear();
        case BINARY:
            throw new UnsupportedOperationException("The BINARY search mode is not implemented yet!");
        default:
            throw new UnsupportedEncodingException("Unknown search mode: " + commandLineOptions.getSearchMode());
        }
    }

    private boolean setFirstCommit() throws IOException {
        String startCommit = commandLineOptions.getStartCommit();
        if (!setCurrentNameOSandEE(startCommit)) {
            printRed("Could not find OS commit for " + startCommit);
            return false;
        }
        currentCommitOS = getCommit(repoOS, walkOS, currentNameOS);
        if (isEE) {
            currentCommitEE = getCommit(repoEE, walkEE, currentNameEE);
        }
        return true;
    }

    private boolean setNextCommitLinear() throws IOException {
        if (isEE) {
            int tryCount = 0;
            do {
                currentCommitEE = getFirstParent(currentCommitEE, walkEE);
                if (currentCommitEE == null) {
                    print("Reached the last EE commit!");
                    return false;
                }
                if (setCurrentNameOSandEE(currentCommitEE.getName())) {
                    if (tryCount > 0) {
                        System.out.println();
                    }
                    break;
                }
            } while (++tryCount < MAX_EE_COMMIT_SKIPS);
            if (currentNameOS == null) {
                printRed("Didn't find any more OS commits, giving up...");
                return false;
            }
            currentCommitOS = getCommit(repoOS, walkOS, currentNameOS);
        } else {
            currentCommitOS = getFirstParent(currentCommitOS, walkOS);
            if (currentCommitOS == null) {
                print("Reached the last OS commit!");
                return false;
            }
        }
        return true;
    }

    private boolean setCurrentNameOSandEE(String currentName) throws IOException {
        currentNameOS = currentName;
        if (isEE) {
            currentNameOS = HEAD.equals(currentName) ? HEAD : commits.get(currentName);
            if (NOT_AVAILABLE.equals(currentNameOS) || currentNameOS == null) {
                printYellow("There is no OS commit for EE %s", asString(getCommit(repoEE, walkEE, currentName)));
                return false;
            }
            currentNameEE = currentName;
            return true;
        }
        return true;
    }

    private boolean executeTest(File projectRoot, List<String> goals, int retryCount)
            throws MavenInvocationException {
        String message = retriesOnTestSuccess > 1 ? "[%s] Executing %s (%d/%d)... " : "[%s] Executing %s... ";
        System.out.printf(message, isEE ? "EE" : "OS", commandLineOptions.getTestClass(), retryCount,
                retriesOnTestSuccess);
        if (isDry) {
            printRed("FAILURE (dry run)");
            return false;
        }

        InvocationRequest request = new DefaultInvocationRequest().setBatchMode(true).setBaseDirectory(projectRoot)
                .setPomFile(new File(projectRoot, "pom.xml")).setGoals(goals);

        long started = System.nanoTime();
        InvocationResult result = invoker.execute(request);
        long elapsedSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - started);

        String errorMsg = outputHandler.findErrors();
        boolean success = errorMsg == null && result.getExitCode() == 0;

        if (success) {
            printGreen("SUCCESS (%d seconds)", elapsedSeconds);
        } else {
            printRed("FAILURE (%d seconds)", elapsedSeconds);
            printRed(errorMsg);
        }
        if (isDebug()) {
            print("\n================================================================================");
            print("================================= Maven output =================================");
            print("================================================================================");
            outputHandler.printAll();
            print("================================================================================\n");
        }
        outputHandler.clear();

        return success;
    }
}