
com.alipay.sofa.koupleless.base.build.plugin.KouplelessBasePackageFacadeMojo Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.alipay.sofa.koupleless.base.build.plugin;
import com.alipay.sofa.koupleless.base.build.plugin.constant.Constants;
import com.alipay.sofa.koupleless.base.build.plugin.model.ArtifactItem;
import com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.License;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
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.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.alipay.sofa.koupleless.base.build.plugin.KouplelessBasePackageFacadeMojo.JVMFileTypeEnum.JAVA;
import static com.alipay.sofa.koupleless.base.build.plugin.KouplelessBasePackageFacadeMojo.JVMFileTypeEnum.KOTLIN;
import static com.alipay.sofa.koupleless.base.build.plugin.common.FileUtils.createNewDirectory;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.CollectionUtils.nonNull;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils.getAllBundleArtifact;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils.getDependencyArtifacts;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils.getPomFileOfBundle;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils.getRootProject;
import static com.alipay.sofa.koupleless.base.build.plugin.utils.MavenUtils.invoke;
/**
* @author [email protected]
* @version $Id: KouplelessBasePackageFacadeMojo.java, v 0.1 2024年07月15日 11:58 立蓬 Exp $
*/
@Mojo(name = "packageFacade", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class KouplelessBasePackageFacadeMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject mavenProject;
@Component
private MavenSession mavenSession;
@Parameter(defaultValue = "${project.basedir}", required = true)
private File baseDir;
@Parameter(defaultValue = "${project.version}")
private String facadeVersion;
@Parameter(defaultValue = "facadeArtifact")
private String facadeArtifactId;
@Parameter(defaultValue = "${project.groupId}", required = true)
private String facadeGroupId;
@Parameter(defaultValue = "")
private LinkedHashSet jvmFiles = new LinkedHashSet<>();
@Parameter(defaultValue = "true")
private String cleanAfterPackageFacade;
@Parameter(defaultValue = "17")
private String jvmTarget;
private static final List SUPPORT_FILE_TYPE_TO_COPY = Stream.of(JAVA)
.collect(Collectors.toList());
private File facadeRootDir;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
createBaseFacade();
} catch (Throwable t) {
getLog().error(t);
throw new RuntimeException(t);
} finally {
clearFacadeRootDir();
}
}
private void createBaseFacade() throws Exception {
createFacadeRootDir();
extractBaseFacades();
installBaseFacades();
moveToOutputs();
}
protected void moveToOutputs() throws IOException {
File outputsDir = new File(baseDir, "outputs");
createNewDirectory(outputsDir);
File facadeTargetDir = new File(facadeRootDir, "target");
if (!facadeTargetDir.exists()) {
throw new RuntimeException(
"facade target dir not exists: " + facadeTargetDir.getAbsolutePath());
}
File[] targetFiles = facadeTargetDir.listFiles();
for (File f : targetFiles) {
if (f.getName().endsWith(".jar")) {
File newFile = new File(outputsDir, f.getName());
Files.copy(f.toPath(), newFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
getLog().info("copy " + f.getAbsolutePath() + " to " + newFile.getAbsolutePath()
+ " success.");
}
}
File newPomFile = new File(outputsDir, "pom.xml");
Files.copy(getPomFileOfBundle(facadeRootDir).toPath(), newPomFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
getLog().info("copy pom.xml to " + newPomFile.getAbsolutePath() + " success.");
}
protected void extractBaseFacades() throws Exception {
//1. 复制指定的jvm文件到该module
copyMatchedJVMFiles();
getLog().info("copy supported jvm files success.");
//2. 配置pom
configPom();
}
private void configPom() throws MojoExecutionException {
Model pom = new Model();
// 设置 xml 头
pom.setModelEncoding("UTF-8");
pom.setVersion("1.0");
pom.setModelVersion("4.0.0");
// 设置 groupId, artifactId, version
pom.setGroupId(facadeGroupId);
pom.setArtifactId(facadeArtifactId);
pom.setVersion(facadeVersion);
// 配置 license
License license = new License();
license.setName("The Apache License, Version 2.0");
license.setUrl("http://www.apache.org/licenses/LICENSE-2.0.txt");
pom.setLicenses(Collections.singletonList(license));
// 配置 properties
Properties properties = this.mavenProject.getProperties();
properties.putIfAbsent("maven-source-plugin.version", "3.2.1");
pom.setProperties(properties);
// 配置依赖,全都设置成 provided
Set baseModuleArtifacts = getAllBundleArtifact(this.mavenProject);
getLog().info("find maven module of base: " + baseModuleArtifacts);
Set dependencyArtifacts = getDependencyArtifacts(this.mavenProject);
List dependencies = nonNull(dependencyArtifacts).stream()
// 过滤出不属于项目的依赖
.filter(d -> baseModuleArtifacts.stream().noneMatch(
baseModule -> Objects.equals(baseModule.getGroupId(), d.getGroupId())
&& Objects.equals(baseModule.getArtifactId(), d.getArtifactId())))
// 过滤出 scope 不是 test, system 的依赖
.filter(d -> !"test".equals(d.getScope()) && !"system".equals(d.getScope())).map(d -> {
Dependency res = MavenUtils.createDependency(d);
res.setScope("provided");
Exclusion exclusion = new Exclusion();
exclusion.setArtifactId("*");
exclusion.setGroupId("*");
res.setExclusions(Collections.singletonList(exclusion));
return res;
}).collect(Collectors.toList());
pom.setDependencies(dependencies);
// 配置 build
Model baseFacadePomTemplate = MavenUtils.buildPomModel(
this.getClass().getClassLoader().getResourceAsStream("base-facade-pom-template.xml"));
Build build = baseFacadePomTemplate.getBuild().clone();
pom.setBuild(build);
// 配置 maven-compiler-plugin 中的 jdk 版本
Plugin mavenCompilerPlugin = build.getPlugins().stream()
.filter(it -> it.getArtifactId().equals("maven-compiler-plugin")).findFirst().get();
Xpp3Dom mavenCompilerConfig = (Xpp3Dom) mavenCompilerPlugin.getConfiguration();
mavenCompilerConfig.getChild("source").setValue(jvmTarget);
mavenCompilerConfig.getChild("target").setValue(jvmTarget);
MavenUtils.writePomModel(getPomFileOfBundle(facadeRootDir), pom);
}
private void copyMatchedJVMFiles() throws IOException {
List allSupportedJVMFiles = getSupportedJVMFiles(
getRootProject(this.mavenProject).getBasedir());
for (File file : allSupportedJVMFiles) {
JVMFileTypeEnum type = getMatchedType(file);
String fullClassName = type.parseFullClassName(file);
if (shouldCopy(fullClassName)) {
File newFile = new File(facadeRootDir.getAbsolutePath() + File.separator
+ type.parseRelativePath(file));
if (!newFile.getParentFile().exists()) {
newFile.getParentFile().mkdirs();
}
Files.copy(file.toPath(), newFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
getLog().info("copy file from " + file.getAbsolutePath() + " to "
+ newFile.getAbsolutePath() + " success.");
}
}
}
protected static List getSupportedJVMFiles(File baseDir) {
List supportedJVMFiles = new ArrayList<>();
getSupportedJVMFiles(baseDir, supportedJVMFiles);
return supportedJVMFiles;
}
private static void getSupportedJVMFiles(File baseDir, List supportedJVMFiles) {
File[] files = baseDir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
getSupportedJVMFiles(file, supportedJVMFiles);
} else if (null != getMatchedType(file)) {
supportedJVMFiles.add(file);
}
}
}
private static JVMFileTypeEnum getMatchedType(File file) {
for (JVMFileTypeEnum type : SUPPORT_FILE_TYPE_TO_COPY) {
if (type.matches(file)) {
return type;
}
}
return null;
}
private boolean shouldCopy(String fullClassName) {
for (String classPattern : jvmFiles) {
if (classPattern.equals(fullClassName)) {
return true;
}
if (classPattern.endsWith(Constants.PACKAGE_PREFIX_MARK)) {
classPattern = StringUtils.removeEnd(classPattern, Constants.PACKAGE_PREFIX_MARK);
if (fullClassName.startsWith(classPattern)) {
return true;
}
}
}
return false;
}
protected void installBaseFacades() throws MojoExecutionException, MavenInvocationException {
try {
InvocationResult result = invoke(mavenSession, "install",
getPomFileOfBundle(facadeRootDir));
if (result.getExitCode() != 0) {
throw new MojoExecutionException("execute mvn install failed for base facades",
result.getExecutionException());
}
} catch (Exception e) {
getLog().error("execute mvn install failed for base facades", e);
throw e;
}
getLog().info("package base facades success.");
}
protected void createFacadeRootDir() throws IOException {
//0. 创建一个空maven工程
File rootDir = new File(baseDir, facadeArtifactId);
createNewDirectory(rootDir);
File facadePom = new File(rootDir, "pom.xml");
if (!facadePom.exists()) {
facadePom.createNewFile();
}
getLog().info("create base dependency directory success." + rootDir.getAbsolutePath());
facadeRootDir = rootDir;
}
protected void clearFacadeRootDir() {
if (Boolean.parseBoolean(cleanAfterPackageFacade) && facadeRootDir != null) {
FileUtils.deleteQuietly(facadeRootDir);
}
}
enum JVMFileTypeEnum {
JAVA("java", ".java", StringUtils.join(new String[] { "src", "main",
"java" },
File.separator) + File.separator), KOTLIN("kotlin", ".kt",
StringUtils.join(
new String[] { "src",
"main",
"kotlin" },
File.separator)
+ File.separator);
private String name;
private String suffix;
private String parentRootDir;
JVMFileTypeEnum(String name, String suffix, String parentRootDir) {
this.name = name;
this.suffix = suffix;
this.parentRootDir = parentRootDir;
}
public boolean matches(File file) {
String absPath = file.getAbsolutePath();
boolean inParentRootDir = absPath.contains(parentRootDir);
boolean onlyOneParentRootDir = absPath.indexOf(parentRootDir) == absPath
.lastIndexOf(parentRootDir);
boolean matchedType = absPath.endsWith(suffix);
return inParentRootDir && onlyOneParentRootDir && matchedType;
}
public String parseFullClassName(File file) {
if (!matches(file)) {
return null;
}
String absPath = file.getAbsolutePath();
return StringUtils.removeEnd(StringUtils.substringAfter(absPath, parentRootDir), suffix)
.replace(File.separator, ".");
}
public String parseRelativePath(File file) {
if (!matches(file)) {
return null;
}
String absPath = file.getAbsolutePath();
return parentRootDir + StringUtils.substringAfter(absPath, parentRootDir);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy