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

com.worksap.tools.spotbugs.maven.incremental.SpotBugsMojo Maven / Gradle / Ivy

/*
 * Copyright 2019 (c) Works Applications Co.,Ltd.
 *
 * 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.worksap.tools.spotbugs.maven.incremental;

import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.eclipse.jgit.lib.Constants;

/**
 * A mojo to generate {@code -onlyAnalyze} parameter for spotbugs-maven-plugin. Generated parameter
 * will be set as property of current Maven execution.
 *
 * 

Execute this mojo before you run spotbugs-maven-plugin, then you can check analysis only for * updated Java classes. * *

 *    <plugin>
 *      <groupId>com.worksap.tools</groupId>
 *      <artifactId>incremental-analysis-maven-plugin</artifactId>
 *      <version>0.1.0-SNAPSHOT</version>
 *      <executions>
 *        <execution>
 *          <phase>verify</phase>
 *          <goals>
 *            <goal>spotbugs</goal>
 *          </goals>
 *        </execution>
 *      </executions>
 *    </plugin>
 *    <plugin>
 *      <groupId>org.codehaus.mojo</groupId>
 *      <artifactId>spotbugs-maven-plugin</artifactId>
 *      <executions>
 *        <execution>
 *          <phase>verify</phase>
 *          <goals>
 *            <goal>spotbugs</goal>
 *          </goals>
 *        </execution>
 *      </executions>
 *    </plugin>
* * @author Kengo TODA ([email protected]) */ @Mojo( name = "spotbugs", threadSafe = false, requiresProject = true, defaultPhase = LifecyclePhase.VERIFY) public class SpotBugsMojo extends AbstractMojo { private final GitUpdatedJavaCodeDetector detector; @Parameter(property = "project", required = true) private MavenProject project; @Parameter(defaultValue = "spotbugs.onlyAnalyze") private String propertyToAnalyze; @Parameter(defaultValue = "spotbugs.skip") private String propertyToSkip; @Parameter(defaultValue = Constants.HEAD, property = "incremental.spotbugs.source") private String source; @Parameter(defaultValue = "refs/heads/master", property = "incremental.spotbugs.target") private String target; @Parameter(defaultValue = "false", property = "incremental.spotbugs.skip") private boolean skip; /** Constructor for production */ public SpotBugsMojo() { this.detector = new GitUpdatedJavaCodeDetector(); } /** Constructor for unit test */ SpotBugsMojo(GitUpdatedJavaCodeDetector detector) { this.detector = Objects.requireNonNull(detector); } @Override public void execute() throws MojoExecutionException { Log log = getLog(); if (skip) { log.info("Skip generating list of target classes for SpotBugs."); return; } Stream updatedJavaCodes; try { if (!detector.detectDifference(project.getBasedir().toPath(), target, source)) { log.info( String.format( "No commit found between %s and %s, so skip generating list of target classes for SpotBugs", target, source)); return; } log.info("Start generating list of target classes for SpotBugs..."); updatedJavaCodes = detector.detectUpdatedCode(project.getBasedir().toPath(), target, source); } catch (IOException e) { throw new MojoExecutionException("Failed to list updated Java code", e); } List targetClasses = codeToClass(getCompileSourceRoots(), updatedJavaCodes).collect(Collectors.toList()); if (targetClasses.isEmpty()) { project.getModel().addProperty(propertyToSkip, "true"); log.info("No updated Java class found, static analysis will be skipped."); } else { String targetClassList = targetClasses.stream().collect(Collectors.joining(",")); project.getModel().addProperty(propertyToAnalyze, targetClassList); if (log.isDebugEnabled()) { targetClasses.forEach( className -> { log.debug("Updated class: " + className); }); } log.info("Successfully generated list of target classes for SpotBugs."); } } /** * @param compileSourceRoots A non-null collection of compile source root. * @param updatedJavaCodes A non-null stream of updated Java codes. * @return {@code Stream} of name of updated classes, such as {@literal com.worksap.ClassName} */ @VisibleForTesting Stream codeToClass(Collection compileSourceRoots, Stream updatedJavaCodes) { assert compileSourceRoots != null; assert updatedJavaCodes != null; Stream relativeJavaCodePaths = updatedJavaCodes .map( javaCode -> compileSourceRoots.stream() .filter(javaCode::startsWith) .map(root -> root.relativize(javaCode)) .findFirst()) .flatMap(this::streamFrom) .map(Path::toString); ClassSearcher searcher = new ClassSearcher(Paths.get(project.getBuild().getOutputDirectory())); return searcher.search(relativeJavaCodePaths); } /** @return A non-null set of compile source roots. Each entry should be absolute {@link Path}. */ private Set getCompileSourceRoots() { return project.getCompileSourceRoots().stream() .map(Paths::get) .map(Path::toAbsolutePath) .collect(Collectors.toSet()); } /** * A missing part in Java8: map {@link Optional} to {@link Stream}. * * @param optional A non-null {@link Optional} to map. * @return A non-null mapped {@link Stream}. */ private Stream streamFrom(Optional optional) { if (optional.isPresent()) { return Stream.of(optional.get()); } else { return Stream.empty(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy