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

io.github.skauppin.maven.buildcache.BuildCachePluginManager Maven / Gradle / Ivy

package io.github.skauppin.maven.buildcache;

import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.DefaultBuildPluginManager;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.PluginManagerException;
import org.codehaus.plexus.component.annotations.Component;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import io.github.skauppin.maven.buildcache.ProjectBuildStatus.Phase;

@Component(role = BuildPluginManager.class)
public class BuildCachePluginManager extends DefaultBuildPluginManager implements Initializable {

  @Requirement
  private Logger logger;

  @Requirement
  private ExecutionTimeRegister executionTimeRegister;

  @Requirement
  private BuildCache buildCache;

  private MojoExecutor delegate = (s, m) -> super.executeMojo(s, m);

  private List phaseExecutions;

  @Override
  public void initialize() throws InitializationException {
    phaseExecutions =
        Arrays.asList(new MainCompileExecution(buildCache), new TestCompileExecution(buildCache),
            new TestExecution(buildCache), new IntegrationTestExecution(buildCache));
  }

  @Override
  public void executeMojo(MavenSession session, MojoExecution mojoExecution)
      throws MojoFailureException, MojoExecutionException, PluginConfigurationException,
      PluginManagerException {

    StopWatch watch = StopWatch.createStarted();

    boolean shouldDelegate = execute(session, mojoExecution);
    if (shouldDelegate) {
      delegate.executeMojo(session, mojoExecution);
    }

    executionTimeRegister.record(session.getCurrentProject(), mojoExecution, watch.getTime(),
        !shouldDelegate);
  }

  private boolean execute(MavenSession session, MojoExecution mojoExecution)
      throws MojoFailureException, MojoExecutionException, PluginConfigurationException,
      PluginManagerException {

    if (buildCache.isInitializationError()) {
      throw new MojoExecutionException(buildCache.getInitializationErrorMessage());
    }

    if (!buildCache.isInitialized()) {
      throw new MojoExecutionException(
          "buildcache has not been initialized. Make sure buildcache extension is"
              + " defined in .mvn/extensions.xml");
    }

    if (buildCache.isBuildCacheDisabled()) {
      return true;
    }

    ProjectBuildStatus projectStatus = buildCache.getProjectStatus(session, mojoExecution);
    if (projectStatus.isBuildCacheDisabled() //
        || MojoExecUtil.isBuildCacheIgnoredPhase(mojoExecution) //
        || projectStatus.isMavenMainSkip()) {
      return true;
    }

    boolean delegate = true;

    for (PhaseExecution phaseExecution : phaseExecutions) {
      if (phaseExecution.shouldExecuteOnMojo(mojoExecution, projectStatus)) {
        ProjectBuildStatus.Phase phase = phaseExecution.getPhase(projectStatus);
        if (phase.isCacheHit()) {
          delegate = false;

        } else if (phaseExecution.shouldUseCachedEntry(projectStatus)) {
          boolean cacheHit = phaseExecution.isCacheHit(session);
          if (cacheHit) {
            logger.info(String.format("buildcache: cache hit - %s (%s)",
                phaseExecution.getCacheHitLogString(), phase.getPhaseHash()));
            phase.setCacheHit();
            delegate = false;
          }
        }

        phase.setVisited();
      }
    }
    return delegate;
  }

  void setLogger(Logger logger) {
    this.logger = logger;
  }

  void setExecutionTimeRegister(ExecutionTimeRegister executionTimeRegister) {
    this.executionTimeRegister = executionTimeRegister;
  }

  void setBuildCache(BuildCache buildCache) {
    this.buildCache = buildCache;
  }

  void setDelegate(MojoExecutor delegate) {
    this.delegate = delegate;
  }

  @FunctionalInterface
  interface MojoExecutor {
    public void executeMojo(MavenSession session, MojoExecution mojoExecution)
        throws MojoFailureException, MojoExecutionException, PluginConfigurationException,
        PluginManagerException;
  }

  abstract static class PhaseExecution {

    final BuildCache buildCache;

    PhaseExecution(BuildCache buildCache) {
      this.buildCache = buildCache;
    }

    public abstract boolean shouldExecuteOnMojo(MojoExecution mojoExecution,
        ProjectBuildStatus projectStatus);

    public abstract ProjectBuildStatus.Phase getPhase(ProjectBuildStatus projectStatus);

    public abstract boolean shouldUseCachedEntry(ProjectBuildStatus projectStatus);

    public abstract boolean isCacheHit(MavenSession session);

    public abstract String getCacheHitLogString();
  }

  static class MainCompileExecution extends PhaseExecution {

    MainCompileExecution(BuildCache buildCache) {
      super(buildCache);
    }

    @Override
    public boolean shouldExecuteOnMojo(MojoExecution mojoExecution,
        ProjectBuildStatus projectStatus) {
      return MojoExecUtil.isCompileRelatedPhase(mojoExecution);
    }

    @Override
    public Phase getPhase(ProjectBuildStatus projectStatus) {
      return projectStatus.getMainCompile();
    }

    @Override
    public boolean shouldUseCachedEntry(ProjectBuildStatus projectStatus) {
      return projectStatus.getMainCompile().notVisited();
    }

    @Override
    public boolean isCacheHit(MavenSession session) {
      return buildCache.useCachedMainClasses(session);
    }

    @Override
    public String getCacheHitLogString() {
      return "using cached main classes";
    }
  }

  static class TestCompileExecution extends PhaseExecution {

    TestCompileExecution(BuildCache buildCache) {
      super(buildCache);
    }

    @Override
    public boolean shouldExecuteOnMojo(MojoExecution mojoExecution,
        ProjectBuildStatus projectStatus) {
      return MojoExecUtil.isTestCompileRelatedPhase(mojoExecution)
          && !projectStatus.isMavenTestSkip();
    }

    @Override
    public Phase getPhase(ProjectBuildStatus projectStatus) {
      return projectStatus.getTestCompile();
    }

    @Override
    public boolean shouldUseCachedEntry(ProjectBuildStatus projectStatus) {
      return projectStatus.getTestCompile().notVisited()
          && projectStatus.getMainCompile().isCacheHit();
    }

    @Override
    public boolean isCacheHit(MavenSession session) {
      return buildCache.useCachedTestClasses(session);
    }

    @Override
    public String getCacheHitLogString() {
      return "using cached test classes";
    }
  }

  static class TestExecution extends PhaseExecution {

    TestExecution(BuildCache buildCache) {
      super(buildCache);
    }

    @Override
    public boolean shouldExecuteOnMojo(MojoExecution mojoExecution,
        ProjectBuildStatus projectStatus) {
      return MojoExecUtil.isTestRelatedPhase(mojoExecution) && isFullTestExecution(projectStatus);
    }

    private boolean isFullTestExecution(ProjectBuildStatus projectStatus) {
      return !projectStatus.isMavenTestSkip() && !projectStatus.isSkipTests()
          && !projectStatus.isTestSubset();
    }

    @Override
    public Phase getPhase(ProjectBuildStatus projectStatus) {
      return projectStatus.getTest();
    }

    @Override
    public boolean shouldUseCachedEntry(ProjectBuildStatus projectStatus) {
      return projectStatus.getTest().notVisited() && projectStatus.getTestCompile().isCacheHit();
    }

    @Override
    public boolean isCacheHit(MavenSession session) {
      return buildCache.isTestExecutionCacheHit(session);
    }

    @Override
    public String getCacheHitLogString() {
      return "test execution success";
    }

  }

  static class IntegrationTestExecution extends PhaseExecution {

    IntegrationTestExecution(BuildCache buildCache) {
      super(buildCache);
    }

    @Override
    public boolean shouldExecuteOnMojo(MojoExecution mojoExecution,
        ProjectBuildStatus projectStatus) {
      return MojoExecUtil.isIntegrationTestRelatedPhase(mojoExecution)
          && isFullIntegrationTestExecution(projectStatus);
    }

    private boolean isFullIntegrationTestExecution(ProjectBuildStatus projectStatus) {
      return !projectStatus.isMavenTestSkip() && !projectStatus.isSkipTests()
          && !projectStatus.isSkipItTests() && !projectStatus.isItTestSubset();
    }

    @Override
    public Phase getPhase(ProjectBuildStatus projectStatus) {
      return projectStatus.getIntegrationTest();
    }

    @Override
    public boolean shouldUseCachedEntry(ProjectBuildStatus projectStatus) {
      return projectStatus.getIntegrationTest().notVisited()
          && projectStatus.getTestCompile().isCacheHit();
    }

    @Override
    public boolean isCacheHit(MavenSession session) {
      return buildCache.isIntegrationTestExecutionCacheHit(session);
    }

    @Override
    public String getCacheHitLogString() {
      return "integration test execution success";
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy