From ce5836b96e87e62167e7b87d0d7bd3b3886204f6 Mon Sep 17 00:00:00 2001 From: CodeNoobKing <67936979+CodeNoobKing@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:38:42 +0800 Subject: [PATCH 1/4] add dynamic adapter --- .../koupleless-base-build-plugin/pom.xml | 101 ++++++++ .../KouplelessBaseBuildPrePackageMojo.java | 222 ++++++++++++++++++ .../build/plugin/common/JarFileUtils.java | 71 ++++++ .../plugin/model/KouplelessAdapterConfig.java | 43 ++++ .../model/MavenDependencyAdapterMapping.java | 43 ++++ .../plugin/model/MavenDependencyMatcher.java | 37 +++ .../src/main/resources/adapter-mapping.yaml | 16 ++ ...KouplelessBaseBuildPrePackageMojoTest.java | 146 ++++++++++++ .../src/test/resources/adapter-mapping.yaml | 12 + .../src/test/resources/demo.jar | Bin 0 -> 2667 bytes koupleless-build-plugin/pom.xml | 97 ++++++++ pom.xml | 1 + 12 files changed, 789 insertions(+) create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/pom.xml create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/common/JarFileUtils.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/KouplelessAdapterConfig.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyAdapterMapping.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyMatcher.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojoTest.java create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/adapter-mapping.yaml create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/demo.jar create mode 100644 koupleless-build-plugin/pom.xml diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml new file mode 100644 index 000000000..2ff9f8f66 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + + com.alipay.sofa.koupleless + koupleless-build-plugin + ${revision} + ../pom.xml + + + koupleless-base-build-plugin + ${revision} + maven-plugin + + + 8 + 8 + UTF-8 + + + + + org.apache.maven + maven-plugin-api + + + org.apache.maven.plugin-tools + maven-plugin-annotations + + + org.apache.maven + maven-aether-provider + + + org.apache.maven + maven-core + + + org.apache.maven + maven-project + + + com.google.guava + guava + + + org.projectlombok + lombok + compile + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-databind + compile + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + org.apache.commons + commons-collections4 + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + 8 + 8 + + + + + + \ No newline at end of file diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java new file mode 100644 index 000000000..a0d55d96e --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java @@ -0,0 +1,222 @@ +/* + * 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; + +/* + * Copyright 2001-2005 The Apache Software Foundation. + * + * 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. + */ + +import com.alipay.sofa.koupleless.base.build.plugin.common.JarFileUtils; +import com.alipay.sofa.koupleless.base.build.plugin.model.KouplelessAdapterConfig; +import com.alipay.sofa.koupleless.base.build.plugin.model.MavenDependencyAdapterMapping; +import com.alipay.sofa.koupleless.base.build.plugin.model.MavenDependencyMatcher; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.common.base.Preconditions; +import lombok.SneakyThrows; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +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.project.MavenProject; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Goal which touches a timestamp file. + * + * @goal touch + * @phase process-sources + */ +@Mojo(name = "add-patch", defaultPhase = LifecyclePhase.PROCESS_CLASSES) +public class KouplelessBaseBuildPrePackageMojo extends AbstractMojo { + + @Parameter(defaultValue = "${project.build.directory}", readonly = true) + File outputDirectory; + + @Parameter(defaultValue = "${project}", required = true, readonly = true) + MavenProject project; + + @Parameter(defaultValue = "${session}", required = true, readonly = true) + MavenSession session; + + @Component + RepositorySystem repositorySystem; + + private ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + + KouplelessAdapterConfig kouplelessAdapterConfig; + + void initKouplelessAdapterConfig() throws Exception { + if (kouplelessAdapterConfig == null) { + InputStream mappingConfigIS = this.getClass().getClassLoader() + .getResourceAsStream("adapter-mapping.yaml"); + + kouplelessAdapterConfig = yamlMapper.readValue(mappingConfigIS, + KouplelessAdapterConfig.class); + + for (MavenDependencyAdapterMapping mappings : + CollectionUtils.emptyIfNull(kouplelessAdapterConfig.getAdapterMappings()) + ) { + + } + } + } + + String getDependencyId(Dependency dependency) { + return dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + + dependency.getVersion() + ":" + dependency.getType() + + (dependency.getClassifier() != null ? ":" + dependency.getClassifier() : ""); + } + + // visible for testing + List getDependenciesToAdd() { + List adapterDependencies = new ArrayList<>(); + if (kouplelessAdapterConfig == null) { + getLog().info("kouplelessAdapterConfig is null, skip adding dependencies."); + return adapterDependencies; + } + + if (kouplelessAdapterConfig.getCommonDependencies() != null) { + adapterDependencies.addAll(kouplelessAdapterConfig.getCommonDependencies()); + } + + Collection adapterMappings = CollectionUtils + .emptyIfNull(kouplelessAdapterConfig.getAdapterMappings()); + for (MavenDependencyAdapterMapping adapterMapping : adapterMappings) { + MavenDependencyMatcher matcher = adapterMapping.getMatcher(); + + if (matcher != null && matcher.getRegexp() != null) { + String regexp = matcher.getRegexp(); + for (Dependency dependency : project.getDependencies()) { + String dependencyId = getDependencyId(dependency); + if (Pattern.compile(regexp).matcher(dependencyId).matches()) { + adapterDependencies.add(adapterMapping.getAdapter()); + } + } + } + + } + return adapterDependencies; + } + + void addDependenciesDynamically() { + if (kouplelessAdapterConfig == null) { + getLog().info("kouplelessAdapterConfig is null, skip adding dependencies."); + return; + } + + Collection dependencies = getDependenciesToAdd(); + for (Dependency dependency : dependencies) { + try { + if (StringUtils.isBlank(dependency.getVersion())) { + dependency.setVersion(project.getVersion()); + } + getLog().debug("start downloading dependency: " + dependency.toString()); + Artifact artifact = downloadAdapterDependency(dependency); + getLog().debug("start add dependency to project root: " + dependency.toString()); + addArtifactToProjectRoot(artifact); + getLog().info("success add dependency: " + dependency.toString()); + } catch (Throwable t) { + getLog().error("error add dependency: " + dependency.toString(), t); + } + } + } + + Artifact downloadAdapterDependency(Dependency dependency) { + DefaultArtifact patchArtifact = new DefaultArtifact(dependency.getGroupId() + ":" + + dependency.getArtifactId() + ":" + + dependency.getVersion()); + + try { + ArtifactRequest artifactRequest = new ArtifactRequest().setArtifact(patchArtifact) + .setRepositories(project.getRemoteProjectRepositories()); + + ArtifactResult artifactResult = repositorySystem.resolveArtifact( + session.getRepositorySession(), artifactRequest); + + Preconditions.checkState(artifactResult.isResolved(), "artifact not resolved."); + return artifactResult.getArtifact(); + } catch (Throwable t) { + getLog().error(t); + throw new RuntimeException(t); + } + } + + void addArtifactToProjectRoot(Artifact artifact) { + File file = artifact.getFile(); + Map entryToContent = JarFileUtils.getFileContentAsLines(file, + Pattern.compile(".*\\.class$")); + for (Map.Entry entry : entryToContent.entrySet()) { + addPatchToProjectRoot(entry.getKey(), entry.getValue()); + } + } + + @SneakyThrows + void addPatchToProjectRoot(String entryName, Byte[] bytes) { + Path outputfile = Paths.get(outputDirectory.getAbsolutePath(), "classes", entryName); + Path parentDir = outputfile.getParent(); + byte[] primitiveByte = new byte[bytes.length]; + for (int i = 0; i < bytes.length; i++) { + primitiveByte[i] = bytes[i]; + } + Files.createDirectories(parentDir); + Files.write(outputfile, primitiveByte); + } + + @Override + public void execute() throws MojoExecutionException { + try { + initKouplelessAdapterConfig(); + addDependenciesDynamically(); + } catch (Throwable t) { + getLog().error(t); + throw new RuntimeException(t); + } + } +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/common/JarFileUtils.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/common/JarFileUtils.java new file mode 100644 index 000000000..f697cbcc8 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/common/JarFileUtils.java @@ -0,0 +1,71 @@ +/* + * 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.common; + +import lombok.SneakyThrows; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.regex.Pattern; + +/** + * jar file utils + * + * @author CodeNoobKing + */ +public class JarFileUtils { + + /** + * get content as file lines inside a bundled jar file. + * + * @param file jarfile + * @param entryPattern the target file. + * @return the content as list of string. + */ + @SneakyThrows + public static Map getFileContentAsLines(File file, Pattern entryPattern) { + Map result = new HashMap<>(); + try (JarInputStream jin = new JarInputStream(new FileInputStream(file))) { + + JarEntry entry = null; + while ((entry = jin.getNextJarEntry()) != null) { + if (!entryPattern.matcher(entry.getName()).matches() || entry.isDirectory()) { + continue; + } + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead = -1; + byte[] data = new byte[1024]; + while ((nRead = jin.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + byte[] byteArray = buffer.toByteArray(); + Byte[] boxing = new Byte[byteArray.length]; + for (int i = 0; i < byteArray.length; i++) { + boxing[i] = byteArray[i]; + } + result.put(entry.getName(), boxing); + } + } + return result; + } + +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/KouplelessAdapterConfig.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/KouplelessAdapterConfig.java new file mode 100644 index 000000000..719119432 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/KouplelessAdapterConfig.java @@ -0,0 +1,43 @@ +/* + * 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.model; + +import lombok.*; +import org.apache.maven.model.Dependency; + +import java.util.List; + +/** + * @author CodeNoobKing + * @date 2024/2/6 + */ +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class KouplelessAdapterConfig { + + /** + * 存在一些通用的依赖,需要用户引入。 + */ + List commonDependencies; + + /** + * 适配的依赖。 + */ + List adapterMappings; +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyAdapterMapping.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyAdapterMapping.java new file mode 100644 index 000000000..5cb44f538 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyAdapterMapping.java @@ -0,0 +1,43 @@ +/* + * 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.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.maven.model.Dependency; + +/** + * @author CodeNoobKing + * @date 2024/2/6 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class MavenDependencyAdapterMapping { + /** + * 匹配用户的以来。 + */ + private MavenDependencyMatcher matcher; + + /** + * 适配的依赖。 + */ + private Dependency adapter; +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyMatcher.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyMatcher.java new file mode 100644 index 000000000..b9c8cc041 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/model/MavenDependencyMatcher.java @@ -0,0 +1,37 @@ +/* + * 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.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author CodeNoobKing + * @date 2024/2/6 + */ +@NoArgsConstructor +@AllArgsConstructor +@Data +@Builder +public class MavenDependencyMatcher { + /** + * 用正则表达式匹配用户的依赖。 + */ + private String regexp; +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml new file mode 100644 index 000000000..51ffadf8f --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml @@ -0,0 +1,16 @@ +adapterMappings: + - matcher: + regexp: ".*com\\.ctrip\\.framework\\.apollo:apollo-client.*" + adapter: + groupId: com.alipay.sofa.koupleless + artifactId: koupleless-adapter-apollo + - matcher: + regexp: ".*(com\\.alibaba:dubbo-dependencies-bom:2\\.6|com\\.alibaba:dubbo:2\\.6).*" + adapter: + groupId: com.alipay.sofa.koupleless + artifactId: koupleless-adapter-dubbo2.6 + - matcher: + regexp: ".*spring-boot-starter-log4j2.*" + adapter: + groupId: com.alipay.sofa.koupleless + artifactId: koupleless-adapter-log4j2 \ No newline at end of file diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojoTest.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojoTest.java new file mode 100644 index 000000000..954b92440 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojoTest.java @@ -0,0 +1,146 @@ +/* + * 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.model.MavenDependencyAdapterMapping; +import com.alipay.sofa.koupleless.base.build.plugin.model.KouplelessAdapterConfig; +import com.alipay.sofa.koupleless.base.build.plugin.model.MavenDependencyMatcher; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Dependency; +import org.apache.maven.project.MavenProject; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResult; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +/** + * @author CodeNoobKing + * @date 2024/2/19 + */ +@RunWith(MockitoJUnitRunner.class) +public class KouplelessBaseBuildPrePackageMojoTest { + @InjectMocks + private KouplelessBaseBuildPrePackageMojo mojo; + + // create a tmp directory using os tmp + private File outputDirectory = null; + + @Mock + MavenProject project; + + @Mock + MavenSession session; + + @Mock + RepositorySystem repositorySystem; + + @Test + public void testLazyInitKouplelessAdapterConfig() throws Exception { + Dependency mockDependency = new Dependency(); + mockDependency.setGroupId("XXX"); + mockDependency.setArtifactId("YYY"); + mockDependency.setVersion("ZZZ"); + + List commonDependencies = new ArrayList<>(); + commonDependencies.add(mockDependency); + + List mappings = new ArrayList<>(); + mappings.add(MavenDependencyAdapterMapping.builder().adapter(mockDependency) + .matcher(MavenDependencyMatcher.builder().regexp(".*").build()).build()); + + mojo.initKouplelessAdapterConfig(); + KouplelessAdapterConfig expected = KouplelessAdapterConfig.builder() + .commonDependencies(commonDependencies).adapterMappings(mappings).build(); + + Assert.assertEquals(expected.toString(), mojo.kouplelessAdapterConfig.toString()); + } + + @Test + public void testAddDependencyDynamically() throws Exception { + { + // init the adapter config + Dependency mockDependency = new Dependency(); + mockDependency.setGroupId("XXX"); + mockDependency.setArtifactId("YYY"); + mockDependency.setVersion("ZZZ"); + + List commonDependencies = new ArrayList<>(); + commonDependencies.add(mockDependency); + + List mappings = new ArrayList<>(); + mappings.add(MavenDependencyAdapterMapping.builder().adapter(mockDependency) + .matcher(MavenDependencyMatcher.builder().regexp(".*A:B:C.*").build()).build()); + + mojo.kouplelessAdapterConfig = KouplelessAdapterConfig.builder() + .commonDependencies(commonDependencies).adapterMappings(mappings).build(); + } + + { + // init maven project + List dependencies = new ArrayList<>(); + Dependency mockDependency = new Dependency(); + mockDependency.setGroupId("A"); + mockDependency.setArtifactId("B"); + mockDependency.setVersion("C"); + dependencies.add(mockDependency); + + doReturn(dependencies).when(project).getDependencies(); + } + + { + // mock the repository system + ArtifactResult mockArtifactResult = new ArtifactResult(new ArtifactRequest()); + Artifact mockArtifact = mock(Artifact.class); + mockArtifactResult.setArtifact(mockArtifact); + + URL demoJarUrl = getClass().getClassLoader().getResource("demo.jar"); + doReturn(new File(demoJarUrl.toURI())).when(mockArtifact).getFile(); + doReturn(mockArtifactResult).when(repositorySystem).resolveArtifact(any(), any()); + } + + { + // init output directory + mojo.outputDirectory = Files.createTempDirectory("mojotest").toFile(); + } + + mojo.execute(); + + { + Assert.assertTrue(Paths + .get(mojo.outputDirectory.getAbsolutePath(), "classes", "com", "example", "demo") + .toFile().exists()); + } + } + +} diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/adapter-mapping.yaml b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/adapter-mapping.yaml new file mode 100644 index 000000000..5b6d12e83 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/adapter-mapping.yaml @@ -0,0 +1,12 @@ +commonDependencies: + - groupId: XXX + artifactId: YYY + version: ZZZ + +adapterMappings: + - matcher: + regexp: ".*" + adapter: + groupId: XXX + artifactId: YYY + version: ZZZ \ No newline at end of file diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/demo.jar b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/resources/demo.jar new file mode 100644 index 0000000000000000000000000000000000000000..fcb018d458876b0e488d8bc77c4897979eeb005f GIT binary patch literal 2667 zcmWIWW@h1HVBlb2xLv3k!GHuffoxyb5Jz24KR5jVpfVAlG7hk^KQ6a<>VVSIfEXx> zQ0D9C=jrAe9HQszmfEwC>yUv6!*_>&5C0zE&Wu%we!ZymR#I2Bfj_6(`lY_y|Mu?M zvEyFhbt}H|D5JQ{xI-t{xRx%pSgIMMbWX2cfltxh=z(T${X6ljrJ;@AV!nR6!5efX zyF*U;zl-(7x7PQ(3ul~6{S>jEXG@-P+p=8_^4HxHZmbNN8szbj4dh;kw{D?%iv{T3 zpJc^|;6$72Ehv_JE zTNLnXgu4+EouJ@pjoa?Mgpq;aE)xTT8c;VVcwqK`mAe4_wnuppjO&R=FW$U4htP7HU!Ud{vPyo5*KIC*OVxi0E;hM%EUHbxz~H`^|bw0 zgICM_V*a&QD{Zbv=PHGoip4i8Kd0SW_y6DTzw8I1Wm;A|c5QeoU|k`ZFn!_2xXh=1 z64R{KxAT16C+XSt)g$xg$}`EC>s{tt^t3kWJkfSYYra4K>vv(IJjVl*iuB&Q+MQF8 zKY8Z^Kg;sRh0BB!SA8`lG5FH0n1Hk!7SrEd z>(Ofw-C1%;$WUg+CACkBKXvE)HGkiGJ>kY)ovpz;WmY*|JhVJYI{fO&|0+giPmkX4 z-|=zN{ocQ)l9Oe`ml}6oG}qEy`=q(6$gQ=~SyyNA!Ga%CJ~LG7zIiNKIpgOqnfoV? z7?#M#CK{FawacBD)>4@MuI|@Njjnr(xMMT=Y%D9^SJb~cYyap@$G;_a4hv>|ng3%w z+p}6Bwkd^OJ&%gMtKaBfbw?#9q@6L0BU+fr$!75mr=MCIYd-Jx;>p;SeK$U0*SxRo zBDQQ-j4x*-@xB%@-y^s9$mRLrH;z3kc_a3L0g-J%@w~r!(j_@y{K^2a7!W5SMQK4% zenDzcNoH!Xww}hRGv3#|bu><#^zqXE@C{$21$&!d-8! zNqf2ecm5Zxz31GVq`z|PRPM-oqZ4breEFQ~McllTqn>3aoLcka&%0^+)05*>6_!^= zm%X@~cf4#mW4WQ6|Bv~%XS1qZn>4Mrbkj*iS=AW^TNSGo7rkU!9ll$zf5UmlZ`(Q? zr5bqtnn} z-s4)a#bjN~sXGg$4_-;uIF}(KA@!Q;Uh~g2kA*gIf9v$MYtni3#HAIpBrcqq<~Ngh;g$XayCruC7KIw$I2{#!A^om;ti#%KFG}C+ z=@8!a<7ef*A9dyT(>0|+p3IQ;;So(J4$j(t?2Y!m3E%protof!p{<7V*Vl=0)r(CO z7&j#TbKg02O>U{`k52h#Zys-4+qUe0b>8RQ2M+GezwqQTG;eR;{^4>%?~7$fKGiTO%JH@4322_|5i9^%Lv=OyBsa;eSEr$$%4flJ(a& zX>GMJSUqi0q(Oy(XhgN!^B#eum-afgLTjez?AhWK>YU~I-?}DYx_Z5-8>|fv_Hti3^d8y>+xs`gr^3ee~G~ zO1aOy^iS#QG89evT>LdiTd0`3C}`)Ypzbc?&!2O6zwmtFDQ*5M!`x*1$9V@ZjWaTd zFypQjfo=f;0fx7ZAR3M|kqdTwc7f_O2mt9+238HoI-xZP!eaCq2vpA@v*3Z6s`n6@ z>oGMW7b?h}KzD@#%p7n`?Z9sklAmzbi!cM1G=9StBv@-n + + 4.0.0 + + com.alipay.sofa.koupleless + koupleless-runtime + ${revision} + ../pom.xml + + + koupleless-build-plugin + ${revision} + pom + + + 8 + 8 + UTF-8 + + + + + + org.apache.maven + maven-plugin-api + 3.9.0 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.8.1 + + + org.apache.maven + maven-aether-provider + 3.3.9 + + + org.apache.maven + maven-core + 3.8.1 + + + org.apache.maven + maven-project + 2.2.1 + + + com.google.guava + guava + 31.0.1-jre + + + org.projectlombok + lombok + 1.18.30 + compile + + + com.fasterxml.jackson.core + jackson-databind + 2.13.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.13.5 + compile + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.5 + + + org.apache.commons + commons-collections4 + 4.4 + + + junit + junit + 4.13.1 + test + + + org.mockito + mockito-core + test + 4.5.1 + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 54d9bc0fc..a17693762 100644 --- a/pom.xml +++ b/pom.xml @@ -82,6 +82,7 @@ koupleless-adapter-ext koupleless-base-loader koupleless-ext + koupleless-base-plugin From ed91f1a072721bef4545607d5ebcdc887ab8792d Mon Sep 17 00:00:00 2001 From: CodeNoobKing <67936979+CodeNoobKing@users.noreply.github.com> Date: Tue, 12 Mar 2024 17:10:58 +0800 Subject: [PATCH 2/4] auto add groupId and version --- .../koupleless-base-build-plugin/pom.xml | 6 +++ .../KouplelessBaseBuildPrePackageMojo.java | 45 +++++++++++-------- .../src/main/resources/adapter-mapping.yaml | 11 ++--- .../src/main/resources/project.properties | 2 + .../base/build/plugin/CommonTest.java | 35 +++++++++++++++ koupleless-build-plugin/pom.xml | 5 +++ pom.xml | 2 +- 7 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/project.properties create mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml index 2ff9f8f66..0ae122c26 100644 --- a/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml +++ b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml @@ -80,6 +80,12 @@ + + + src/main/resources + true + + org.apache.maven.plugins diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java index a0d55d96e..c452ea00c 100644 --- a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/java/com/alipay/sofa/koupleless/base/build/plugin/KouplelessBaseBuildPrePackageMojo.java @@ -61,10 +61,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.regex.Pattern; /** @@ -77,35 +74,42 @@ public class KouplelessBaseBuildPrePackageMojo extends AbstractMojo { @Parameter(defaultValue = "${project.build.directory}", readonly = true) - File outputDirectory; + File outputDirectory; @Parameter(defaultValue = "${project}", required = true, readonly = true) - MavenProject project; + MavenProject project; @Parameter(defaultValue = "${session}", required = true, readonly = true) - MavenSession session; + MavenSession session; @Component - RepositorySystem repositorySystem; + RepositorySystem repositorySystem; - private ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + private ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); KouplelessAdapterConfig kouplelessAdapterConfig; + String defaultGroupId = ""; + String defaultVersion = ""; + void initKouplelessAdapterConfig() throws Exception { if (kouplelessAdapterConfig == null) { InputStream mappingConfigIS = this.getClass().getClassLoader() - .getResourceAsStream("adapter-mapping.yaml"); + .getResourceAsStream("adapter-mapping.yaml"); kouplelessAdapterConfig = yamlMapper.readValue(mappingConfigIS, - KouplelessAdapterConfig.class); + KouplelessAdapterConfig.class); - for (MavenDependencyAdapterMapping mappings : - CollectionUtils.emptyIfNull(kouplelessAdapterConfig.getAdapterMappings()) - ) { + for (MavenDependencyAdapterMapping mappings : CollectionUtils + .emptyIfNull(kouplelessAdapterConfig.getAdapterMappings())) { } } + + Properties properties = new Properties(); + properties.load(getClass().getClassLoader().getResourceAsStream("project.properties")); + defaultGroupId = properties.getProperty("project.groupId"); + defaultVersion = properties.getProperty("project.version"); } String getDependencyId(Dependency dependency) { @@ -127,7 +131,7 @@ List getDependenciesToAdd() { } Collection adapterMappings = CollectionUtils - .emptyIfNull(kouplelessAdapterConfig.getAdapterMappings()); + .emptyIfNull(kouplelessAdapterConfig.getAdapterMappings()); for (MavenDependencyAdapterMapping adapterMapping : adapterMappings) { MavenDependencyMatcher matcher = adapterMapping.getMatcher(); @@ -155,7 +159,10 @@ void addDependenciesDynamically() { for (Dependency dependency : dependencies) { try { if (StringUtils.isBlank(dependency.getVersion())) { - dependency.setVersion(project.getVersion()); + dependency.setVersion(defaultVersion); + } + if (StringUtils.isBlank(dependency.getGroupId())) { + dependency.setGroupId(defaultGroupId); } getLog().debug("start downloading dependency: " + dependency.toString()); Artifact artifact = downloadAdapterDependency(dependency); @@ -175,10 +182,10 @@ Artifact downloadAdapterDependency(Dependency dependency) { try { ArtifactRequest artifactRequest = new ArtifactRequest().setArtifact(patchArtifact) - .setRepositories(project.getRemoteProjectRepositories()); + .setRepositories(project.getRemoteProjectRepositories()); ArtifactResult artifactResult = repositorySystem.resolveArtifact( - session.getRepositorySession(), artifactRequest); + session.getRepositorySession(), artifactRequest); Preconditions.checkState(artifactResult.isResolved(), "artifact not resolved."); return artifactResult.getArtifact(); @@ -191,7 +198,7 @@ Artifact downloadAdapterDependency(Dependency dependency) { void addArtifactToProjectRoot(Artifact artifact) { File file = artifact.getFile(); Map entryToContent = JarFileUtils.getFileContentAsLines(file, - Pattern.compile(".*\\.class$")); + Pattern.compile(".*\\.class$")); for (Map.Entry entry : entryToContent.entrySet()) { addPatchToProjectRoot(entry.getKey(), entry.getValue()); } diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml index 51ffadf8f..9afe4e606 100644 --- a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/adapter-mapping.yaml @@ -2,15 +2,16 @@ adapterMappings: - matcher: regexp: ".*com\\.ctrip\\.framework\\.apollo:apollo-client.*" adapter: - groupId: com.alipay.sofa.koupleless artifactId: koupleless-adapter-apollo - matcher: regexp: ".*(com\\.alibaba:dubbo-dependencies-bom:2\\.6|com\\.alibaba:dubbo:2\\.6).*" adapter: - groupId: com.alipay.sofa.koupleless artifactId: koupleless-adapter-dubbo2.6 - matcher: - regexp: ".*spring-boot-starter-log4j2.*" + regexp: ".*spring-boot-starter-log4j2:(?!2\\.1).*" adapter: - groupId: com.alipay.sofa.koupleless - artifactId: koupleless-adapter-log4j2 \ No newline at end of file + artifactId: koupleless-adapter-log4j2 + - matcher: + regexp: ".*spring-boot-starter-log4j2:2\\.1.*" + adapter: + artifactId: koupleless-adapter-log4j2-springboot2.1.9 diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/project.properties b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/project.properties new file mode 100644 index 000000000..76bf73291 --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/main/resources/project.properties @@ -0,0 +1,2 @@ +project.groupId=${project.groupId} +project.version=${project.version} \ No newline at end of file diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java new file mode 100644 index 000000000..5c071046d --- /dev/null +++ b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java @@ -0,0 +1,35 @@ +/* + * 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 org.junit.Assert; +import org.junit.Test; + +public class CommonTest { + + @Test + public void testRegexp() { + Assert.assertTrue("spring-boot-starter-log4j2:2.1.9.RELEASE" + .matches(".*spring-boot-starter-log4j2:2\\.1.*")); + Assert.assertFalse("spring-boot-starter-log4j2:2.1.9.RELEASE" + .matches(".*spring-boot-starter-log4j2:(?!2\\.1).*")); + Assert.assertTrue("spring-boot-starter-log4j2:2.7.9.RELEASE" + .matches(".*spring-boot-starter-log4j2:(?!2\\.1).*")); + Assert.assertFalse("spring-boot-starter-log4j2:2.7.9.RELEASE" + .matches(".*spring-boot-starter-log4j2:2\\.1.*")); + } +} diff --git a/koupleless-build-plugin/pom.xml b/koupleless-build-plugin/pom.xml index 757dc70b8..da8575b41 100644 --- a/koupleless-build-plugin/pom.xml +++ b/koupleless-build-plugin/pom.xml @@ -14,6 +14,11 @@ ${revision} pom + + koupleless-base-build-plugin + + + 8 8 diff --git a/pom.xml b/pom.xml index a17693762..45a5e418e 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ koupleless-adapter-ext koupleless-base-loader koupleless-ext - koupleless-base-plugin + koupleless-build-plugin From b842ce729c7c8c72a9ae4cc41b37f20aea810d69 Mon Sep 17 00:00:00 2001 From: CodeNoobKing <67936979+CodeNoobKing@users.noreply.github.com> Date: Tue, 12 Mar 2024 20:15:21 +0800 Subject: [PATCH 3/4] by review --- .../koupleless-base-build-plugin/pom.xml | 5 --- .../base/build/plugin/CommonTest.java | 35 ------------------- koupleless-build-plugin/pom.xml | 7 ---- 3 files changed, 47 deletions(-) delete mode 100644 koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml index 0ae122c26..c01ec10d9 100644 --- a/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml +++ b/koupleless-build-plugin/koupleless-base-build-plugin/pom.xml @@ -14,11 +14,6 @@ ${revision} maven-plugin - - 8 - 8 - UTF-8 - diff --git a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java b/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java deleted file mode 100644 index 5c071046d..000000000 --- a/koupleless-build-plugin/koupleless-base-build-plugin/src/test/java/com/alipay/sofa/koupleless/base/build/plugin/CommonTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 org.junit.Assert; -import org.junit.Test; - -public class CommonTest { - - @Test - public void testRegexp() { - Assert.assertTrue("spring-boot-starter-log4j2:2.1.9.RELEASE" - .matches(".*spring-boot-starter-log4j2:2\\.1.*")); - Assert.assertFalse("spring-boot-starter-log4j2:2.1.9.RELEASE" - .matches(".*spring-boot-starter-log4j2:(?!2\\.1).*")); - Assert.assertTrue("spring-boot-starter-log4j2:2.7.9.RELEASE" - .matches(".*spring-boot-starter-log4j2:(?!2\\.1).*")); - Assert.assertFalse("spring-boot-starter-log4j2:2.7.9.RELEASE" - .matches(".*spring-boot-starter-log4j2:2\\.1.*")); - } -} diff --git a/koupleless-build-plugin/pom.xml b/koupleless-build-plugin/pom.xml index da8575b41..f08cb3e44 100644 --- a/koupleless-build-plugin/pom.xml +++ b/koupleless-build-plugin/pom.xml @@ -18,13 +18,6 @@ koupleless-base-build-plugin - - - 8 - 8 - UTF-8 - - From 9bad9fb778d3969137255eb1fbf4412938824e55 Mon Sep 17 00:00:00 2001 From: CodeNoobKing <67936979+CodeNoobKing@users.noreply.github.com> Date: Tue, 12 Mar 2024 20:33:01 +0800 Subject: [PATCH 4/4] trigger diff --- koupleless-build-plugin/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/koupleless-build-plugin/pom.xml b/koupleless-build-plugin/pom.xml index f08cb3e44..cee193d85 100644 --- a/koupleless-build-plugin/pom.xml +++ b/koupleless-build-plugin/pom.xml @@ -19,6 +19,7 @@ + org.apache.maven