主流程说明
插件版本: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"来查找要下载的文件
分别对应文件名和hash值。然后使用“/batch/file?name=%s”对文件下载,例如:http://localhost:9000/batch/file?name=sonar-scanner-engine-shaded-10.1.0.73491-all.jar
接着创建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]
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)