主流程说明

插件版本:3.10-SNAPSHOT

<plugin>
  <groupId>org.sonarsource.scanner.maven</groupId>
  <artifactId>sonar-maven-plugin</artifactId>
  <version>3.10-SNAPSHOT</version>
</plugin>

作为maven的插件首先要定义的就是Mojo。在org.sonarsource.scanner.maven包下有个继承自AbstractMojo的SonarQubeMojo类。

/**
 * Analyze project. SonarQube server must be started.
 */
@Mojo(name = "sonar", requiresDependencyResolution = ResolutionScope.TEST, aggregator = true)
public class SonarQubeMojo extends AbstractMojo {

  @Parameter(defaultValue = "${session}", required = true, readonly = true)
  private MavenSession session;

  /**
   * Set this to 'true' to skip analysis.
   * @since 2.3
   */
  @Parameter(alias = "sonar.skip", property = "sonar.skip", defaultValue = "false")
  private boolean skip;
  
  ... ... 
}

主要看里面的execute()方法

 @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    if (getLog().isDebugEnabled()) {
      setLog(new TimestampLogger(getLog()));
    }

    if (shouldDelayExecution()) {
      getLog().info("Delaying SonarQube Scanner to the end of multi-module project");
      return;
    }
		//读取环境变量SONARQUBE_SCANNER_PARAMS配置的Json字符串并解析
    Properties envProps = Utils.loadEnvironmentProperties(System.getenv());

    //为了后续获取compile阶段的maven配置信息等
    MavenCompilerResolver mavenCompilerResolver = new MavenCompilerResolver(session, lifecycleExecutor, getLog(), toolchainManager);
    MavenProjectConverter mavenProjectConverter = new MavenProjectConverter(getLog(), mavenCompilerResolver, envProps);
    LogHandler logHandler = new LogHandler(getLog());

    //用户读取被加密的配置(属性解密)
    PropertyDecryptor propertyDecryptor = new PropertyDecryptor(getLog(), securityDispatcher);

    //扫描器创建工厂
    ScannerFactory runnerFactory = new ScannerFactory(logHandler, getLog(), runtimeInformation, mojoExecution, session, envProps, propertyDecryptor);
    if (isSkip(runnerFactory.createGlobalProperties())) {
      return;
    }
    /**
    * 创建IsolatedLauncherFactory实例对象作为参数创建EmbeddedScanner对象
    * 
    * 注意:EmbeddedScanner里面包含了从服务器下载jar包,并实例化实例化org.sonar.batch.bootstrapper.Batch[在sonar-scanner-engine中,从服务器上下载]的代码
    * 说明:EmbeddedScanner在sonar-scanner-api.jar包中
    */
    EmbeddedScanner runner = runnerFactory.create();
    
    /**
    * 执行ScannerBootstrapper的execute方法
    *
    new ScannerBootstrapper(getLog(), session, runner, mavenProjectConverter, propertyDecryptor).execute();
  }

ScannerBootstrapper的execute方法:


/**
 * Configure properties and bootstrap using SonarQube scanner API
 */
public class ScannerBootstrapper {

  static final String UNSUPPORTED_BELOW_SONARQUBE_56_MESSAGE = "With SonarQube server prior to 5.6, use sonar-maven-plugin <= 3.3";

  private final Log log;
  private final MavenSession session;
  private final EmbeddedScanner scanner;
  private final MavenProjectConverter mavenProjectConverter;
  private String serverVersion;
  private PropertyDecryptor propertyDecryptor;

  public ScannerBootstrapper(Log log, MavenSession session, EmbeddedScanner scanner, MavenProjectConverter mavenProjectConverter, PropertyDecryptor propertyDecryptor) {
    this.log = log;
    this.session = session;
    this.scanner = scanner;
    this.mavenProjectConverter = mavenProjectConverter;
    this.propertyDecryptor = propertyDecryptor;
  }

  public void execute() throws MojoExecutionException {
    try {
      applyMasks();
      
      // 执行EmbeddedScanner的start方法
      // 该方法首先会初始化sonar.host.url等配置信息,然后创建
      // IsolatedLauncher类型实例对象,在创建该对象的时候会从
      // 服务器上下载jar包,并使用自定义的classLoader来加载并实例化
      // 而创建的IsolatedLauncher其实就是:
      // org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher
      // 其本身在sonar-scanner-api.jar内的sonar-scanner-api-batch.jar中
      // 待需要准备的对象都准备好后,其方法的使命就结束了
      scanner.start();
      
      serverVersion = scanner.serverVersion();

      checkSQVersion();

      if (log.isDebugEnabled()) {
        scanner.setGlobalProperty("sonar.verbose", "true");
      }

      //执行EmbeddedScanner的execute方法
      //其实质是执行launcher(IsolatedLauncher)的execute方法,而launcher的execute方法
      //创建了org.sonar.batch.bootstrapper.Batch实例[在sonar-scanner-engine中,即下载的包中]
      //并执行实例对象execute()方法开始真实的扫描等工作
      // 那我们的实现也需要去到sonar-scanner-engine.jar包
      scanner.execute(collectProperties());
    } catch (Exception e) {
      throw new MojoExecutionException(e.getMessage(), e);
    }
  }
  ... ... 
}



从服务器上下载包

通过"/batch/index"来查找要下载的文件

sonar-maven-plugin插件源码解读_maven

分别对应文件名和hash值。然后使用“/batch/file?name=%s”对文件下载,例如:http://localhost:9000/batch/file?name=sonar-scanner-engine-shaded-10.1.0.73491-all.jar

sonar-maven-plugin插件源码解读_maven_02

接着创建IsolatedClassloader对象来加载jar包,

private IsolatedClassloader createClassLoader(List<File> jarFiles, ClassloadRules maskRules) {
   IsolatedClassloader classloader = new IsolatedClassloader(getClass().getClassLoader(), maskRules);
   classloader.addFiles(jarFiles);
   return classloader;
 }


BatchIsolatedLauncher

Launcher的实现类:org.sonarsource.scanner.api.internal.batch.BatchIsolatedLauncher [sonar-scanner-api-batch.jar]

sonar-maven-plugin插件源码解读_maven_03


public class BatchIsolatedLauncher implements IsolatedLauncher {
    private final BatchFactory factory;

    public BatchIsolatedLauncher() {
        this(new DefaultBatchFactory());
    }

    public BatchIsolatedLauncher(BatchFactory factory) {
        this.factory = factory;
    }

    public void execute(Map<String, String> properties, LogOutput logOutput) {
        //主要做的事情就是通过DefaultBatchFactory创建Batch实例并执行
        this.factory.createBatch(properties, logOutput).execute();
    }

    public String getVersion() {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("sq-version.txt");
        if (is == null) {
            return null;
        } else {
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
                String var3;
                try {
                    var3 = br.readLine();
                } catch (Throwable var6) {
                    try {
                        br.close();
                    } catch (Throwable var5) {
                        var6.addSuppressed(var5);
                    }

                    throw var6;
                }

                br.close();
                return var3;
            } catch (IOException var7) {
                return null;
            }
        }
    }
}

DefaultBatchFactory中创建Batch实例

class DefaultBatchFactory implements BatchFactory {
    private static final String SCANNER_APP_KEY = "sonar.scanner.app";
    private static final String SCANNER_APP_VERSION_KEY = "sonar.scanner.appVersion";

    DefaultBatchFactory() {}

    public Batch createBatch(Map<String, String> properties, LogOutput logOutput) {
        EnvironmentInformation env = new EnvironmentInformation((String)properties.get("sonar.scanner.app"), (String)properties.get("sonar.scanner.appVersion"));
        
        //org.sonar.batch.bootstrapper.Batch[在sonar-scanner-engine中,即下载的包中]
        return Batch.builder().setEnvironment(env).setGlobalProperties(properties).setLogOutput((formattedMessage, level) -> {
            logOutput.log(formattedMessage, Level.valueOf(level.name()));
        }).build();
    }
}

其目的就是创建sonar-scanner-engine中org.sonar.batch.bootstrapper.Batch对象的实例


小结

sonar-maven-plugin插件的主要目的是读取配置,然后创建sonar-scanner-engine中的Batch实例对象,并引导该实例对象的执行。 最后对文件的扫描等处理都在从服务器上下载的sonar-scanner-engine包中(实际名字叫sonar-scanner-engine-shaded)