/amps-maven-plugin/src/main/java/com/atlassian/maven/plugins/amps/MavenGoals.java
Java | 1049 lines | 794 code | 120 blank | 135 comment | 54 complexity | 5e7008797b8a4b14832e17480ccc82b7 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- package com.atlassian.maven.plugins.amps;
- import aQute.bnd.osgi.Constants;
- import com.atlassian.maven.plugins.amps.minifier.MinifierParameters;
- import com.atlassian.maven.plugins.amps.minifier.ResourcesMinifier;
- import com.atlassian.maven.plugins.amps.util.AmpsCreatePluginPrompter;
- import com.atlassian.maven.plugins.amps.util.CreatePluginProperties;
- import com.atlassian.maven.plugins.amps.util.MojoExecutorWrapper;
- import com.atlassian.maven.plugins.amps.util.VersionUtils;
- import com.googlecode.htmlcompressor.compressor.XmlCompressor;
- import org.apache.commons.io.FileUtils;
- import org.apache.commons.io.filefilter.FileFilterUtils;
- import org.apache.commons.io.filefilter.IOFileFilter;
- import org.apache.commons.lang3.StringUtils;
- import org.apache.maven.archetype.common.DefaultPomManager;
- import org.apache.maven.archetype.common.MavenJDOMWriter;
- import org.apache.maven.archetype.common.util.Format;
- import org.apache.maven.model.FileSet;
- import org.apache.maven.model.Model;
- import org.apache.maven.model.Plugin;
- import org.apache.maven.plugin.MojoExecutionException;
- import org.apache.maven.plugin.logging.Log;
- import org.apache.maven.project.MavenProject;
- import org.codehaus.plexus.components.interactivity.PrompterException;
- import org.codehaus.plexus.util.IOUtil;
- import org.codehaus.plexus.util.xml.Xpp3Dom;
- import org.jdom.Document;
- import org.jdom.input.SAXBuilder;
- import org.twdata.maven.mojoexecutor.MojoExecutor.Element;
- import org.twdata.maven.mojoexecutor.MojoExecutor.ExecutionEnvironment;
- import javax.annotation.Nonnull;
- import javax.annotation.Nullable;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStreamWriter;
- import java.io.UncheckedIOException;
- import java.io.Writer;
- import java.nio.charset.Charset;
- 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.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Properties;
- import java.util.Set;
- import java.util.TreeSet;
- import java.util.jar.Manifest;
- import java.util.regex.Matcher;
- import java.util.stream.Stream;
- import static com.atlassian.maven.plugins.amps.BannedDependencies.getBannedElements;
- import static com.atlassian.maven.plugins.amps.product.manager.BaseUrlUtils.getBaseUrl;
- import static com.atlassian.maven.plugins.amps.util.FileUtils.file;
- import static java.lang.String.format;
- import static java.nio.charset.StandardCharsets.UTF_8;
- import static java.nio.file.Files.createDirectories;
- import static java.nio.file.Files.createTempDirectory;
- import static java.nio.file.Files.walk;
- import static java.util.Map.Entry.comparingByKey;
- import static java.util.Objects.requireNonNull;
- import static java.util.stream.Collectors.groupingBy;
- import static java.util.stream.Collectors.joining;
- import static java.util.stream.Collectors.mapping;
- import static java.util.stream.Collectors.toCollection;
- import static java.util.stream.Collectors.toMap;
- import static org.apache.commons.io.FileUtils.deleteDirectory;
- import static org.apache.commons.lang3.StringUtils.isNotBlank;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.artifactId;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.element;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.groupId;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.name;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin;
- import static org.twdata.maven.mojoexecutor.MojoExecutor.version;
- /**
- * Executes specific maven goals
- */
- public class MavenGoals {
- /**
- * Defines a Failsafe/Surefire {@code %regex} pattern which can be used to exclude/include integration tests.
- * <p>
- * Pattern handling for excludes and includes changed in Surefire 2.22, and patterns that don't start with
- * "**/" are now prefixed with it automatically. That means "it/**" becomes "**/it/**", which matches
- * any test with an "it" package anywhere, not just at the root. Regexes are <i>not</i> automatically prefixed,
- * so using a regex pattern here allows us to continue only matching tests with an "it" package at the root.
- * <p>
- * <b>Warning</b>: Surefire's documentation states that all slashes are forward, even on Windows. However,
- * <a href="https://github.com/bturner/surefire-slashes">a simple test</a> proves that that's not the case.
- * <a href="https://issues.apache.org/jira/browse/SUREFIRE-1599">SUREFIRE-1599</a> has been created to track
- * this mismatch between the documentation and the implementation.
- *
- * @since 8.0
- */
- public static final String REGEX_INTEGRATION_TESTS = "%regex[it[/\\\\].*]";
- private static final String ABSTRACT_CLASSES = "**/Abstract*";
- private static final String INNER_CLASSES = "**/*$*";
- private static final String REPORTS_DIRECTORY = "reportsDirectory";
- private final MavenContext ctx;
- private final Log log;
- private final MojoExecutorWrapper mojoExecutorWrapper;
- public MavenGoals(final MavenContext ctx, final MojoExecutorWrapper mojoExecutorWrapper) {
- this.ctx = requireNonNull(ctx);
- this.log = ctx.getLog();
- this.mojoExecutorWrapper = requireNonNull(mojoExecutorWrapper);
- }
- private ExecutionEnvironment executionEnvironment() {
- return ctx.getExecutionEnvironment();
- }
- public MavenProject getContextProject() {
- return ctx.getProject();
- }
- public void executeAmpsRecursively(final String ampsVersion, final String ampsGoal, final Xpp3Dom configuration)
- throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- plugin(
- groupId("com.atlassian.maven.plugins"),
- artifactId("amps-maven-plugin"),
- version(ampsVersion)
- ),
- goal(ampsGoal),
- configuration,
- executionEnvironment());
- }
- public void createPlugin(final String productId, AmpsCreatePluginPrompter createPrompter) throws MojoExecutionException {
- CreatePluginProperties props = null;
- Properties systemProps = System.getProperties();
- if (systemProps.containsKey("groupId")
- && systemProps.containsKey("artifactId")
- && systemProps.containsKey("version")
- && systemProps.containsKey("package")) {
- props = new CreatePluginProperties(systemProps.getProperty("groupId"),
- systemProps.getProperty("artifactId"), systemProps.getProperty("version"),
- systemProps.getProperty("package"), systemProps.getProperty("useOsgiJavaConfig", "N"));
- }
- if (props == null) {
- try {
- props = createPrompter.prompt();
- } catch (PrompterException e) {
- throw new MojoExecutionException("Unable to gather properties", e);
- }
- }
- if (props != null) {
- ExecutionEnvironment execEnv = executionEnvironment();
- Properties userProperties = execEnv.getMavenSession().getUserProperties();
- userProperties.setProperty("groupId", props.getGroupId());
- userProperties.setProperty("artifactId", props.getArtifactId());
- userProperties.setProperty("version", props.getVersion());
- userProperties.setProperty("package", props.getThePackage());
- userProperties.setProperty("useOsgiJavaConfig", props.getUseOsgiJavaConfigInMavenInvocationFormat());
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-archetype-plugin"),
- goal("generate"),
- configuration(
- element(name("archetypeGroupId"), "com.atlassian.maven.archetypes"),
- element(name("archetypeArtifactId"), (productId.equals("all") ? "" : productId + "-") + "plugin-archetype"),
- element(name("archetypeVersion"), VersionUtils.getVersion()),
- element(name("interactiveMode"), "false")
- ),
- execEnv);
- /*
- The problem is if plugin is sub of multiple module project, then the pom file will be add parent section.
- When add parent section to module pom file, maven use the default Format with lineEnding is \r\n.
- This step add \r\n character as the line ending.
- Call the function below to remove cr (\r) character
- */
- correctCrlf(props.getArtifactId());
- File pluginDir = new File(ctx.getProject().getBasedir(), props.getArtifactId());
- if (pluginDir.exists()) {
- File src = new File(pluginDir, "src");
- File test = new File(src, "test");
- File java = new File(test, "java");
- String packagePath = props.getThePackage().replaceAll("\\.", Matcher.quoteReplacement(File.separator));
- File packageFile = new File(java, packagePath);
- File packageUT = new File(packageFile, "ut");
- File packageIT = new File(packageFile, "it");
- File ut = new File(new File(java, "ut"), packagePath);
- File it = new File(new File(java, "it"), packagePath);
- if (packageFile.exists()) {
- try {
- if (packageUT.exists()) {
- FileUtils.copyDirectory(packageUT, ut);
- }
- if (packageIT.exists()) {
- FileUtils.copyDirectory(packageIT, it);
- }
- IOFileFilter filter = FileFilterUtils.and(FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("it")), FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("ut")));
- com.atlassian.maven.plugins.amps.util.FileUtils.cleanDirectory(java, filter);
- } catch (IOException e) {
- //for now just ignore
- }
- }
- }
- }
- }
- /**
- * Helper function to scan all generated folder and list all pom.xml files that need to be re-write to remove \r
- */
- private void correctCrlf(String artifactId) {
- if (ctx != null && ctx.getProject() != null
- && ctx.getProject().getBasedir() != null && ctx.getProject().getBasedir().exists()) {
- File outputDirectoryFile = new File(ctx.getProject().getBasedir(), artifactId);
- if (outputDirectoryFile.exists()) {
- FilenameFilter pomFilter = (dir, name) -> "pom.xml".equals(name);
- File[] pomFiles = outputDirectoryFile.listFiles(pomFilter);
- DefaultPomManager pomManager = new DefaultPomManager();
- for (File pom : pomFiles) {
- processCorrectCrlf(pomManager, pom);
- }
- }
- }
- }
- /**
- * Helper function to re-write pom.xml file with lineEnding \n instead of \r\n
- */
- protected void processCorrectCrlf(DefaultPomManager pomManager, File pom) {
- InputStream inputStream = null;
- Writer outputStreamWriter = null;
- final Model model;
- try {
- model = pomManager.readPom(pom);
- String fileEncoding = StringUtils.isEmpty(model.getModelEncoding()) ? model.getModelEncoding() : "UTF-8";
- inputStream = new FileInputStream(pom);
- SAXBuilder builder = new SAXBuilder();
- Document doc = builder.build(inputStream);
- // The cdata parts of the pom are not preserved from initial to target
- MavenJDOMWriter writer = new MavenJDOMWriter();
- outputStreamWriter = new OutputStreamWriter(new FileOutputStream(pom), fileEncoding);
- Format form = Format.getRawFormat().setEncoding(fileEncoding);
- form.setLineSeparator("\n");
- writer.write(model, doc, outputStreamWriter, form);
- } catch (Exception e) {
- log.error("Have exception when try correct line ending.", e);
- } finally {
- IOUtil.close(inputStream);
- IOUtil.close(outputStreamWriter);
- }
- }
- public void copyBundledDependencies() throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy-dependencies"),
- configuration(
- // Here, runtime means only compile- and runtime-scoped dependencies
- element(name("includeScope"), "runtime"),
- element(name("includeTypes"), "jar"),
- element(name("outputDirectory"), "${project.build.outputDirectory}/META-INF/lib")
- ),
- executionEnvironment()
- );
- }
- /**
- * Uses the Enforcer plugin to check that the current project has no platform modules in {@code compile} scope,
- * even transitively, i.e. it will not bundle any such artifacts.
- *
- * @param banExcludes any dependencies to be allowed, in the form {@code groupId:artifactId[:version][:type]}
- * @throws MojoExecutionException if something goes wrong
- */
- public void validateBannedDependencies(final Set<String> banExcludes) throws MojoExecutionException {
- log.info("validate banned dependencies");
- mojoExecutorWrapper.executeWithMergedConfig(
- plugin(
- groupId("org.apache.maven.plugins"),
- artifactId("maven-enforcer-plugin"),
- version("3.0.0-M3")
- ),
- goal("enforce"),
- configuration(
- element(name("rules"),
- element(name("bannedDependencies"),
- element(name("searchTransitive"), "true"),
- element(name("message"), "make sure platform artifacts are not bundled into plugin"),
- element(name("excludes"), getBannedElements(banExcludes).toArray(new Element[0])))
- )
- ),
- executionEnvironment()
- );
- }
- public void copyTestBundledDependencies(List<ProductArtifact> testBundleExcludes) throws MojoExecutionException {
- StringBuilder sb = new StringBuilder();
- for (ProductArtifact artifact : testBundleExcludes) {
- log.info("excluding artifact from test jar: " + artifact.getArtifactId());
- sb.append(",").append(artifact.getArtifactId());
- }
- String customExcludes = sb.toString();
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy-dependencies"),
- configuration(
- element(name("excludeArtifactIds"), "junit" + customExcludes),
- element(name("useSubDirectoryPerScope"), "true"),
- element(name("outputDirectory"), "${project.build.directory}/testlibs")
- ),
- executionEnvironment()
- );
- File targetDir = new File(ctx.getProject().getBuild().getDirectory());
- File testlibsDir = new File(targetDir, "testlibs");
- File compileLibs = new File(testlibsDir, "compile");
- File testLibs = new File(testlibsDir, "test");
- File testClassesDir = new File(ctx.getProject().getBuild().getTestOutputDirectory());
- File metainfDir = new File(testClassesDir, "META-INF");
- File libDir = new File(metainfDir, "lib");
- try {
- compileLibs.mkdirs();
- testLibs.mkdirs();
- libDir.mkdirs();
- FileUtils.copyDirectory(compileLibs, libDir);
- FileUtils.copyDirectory(testLibs, libDir);
- } catch (IOException e) {
- throw new MojoExecutionException("unable to copy test libs", e);
- }
- }
- public void copyTestBundledDependenciesExcludingTestScope(List<ProductArtifact> testBundleExcludes) throws MojoExecutionException {
- StringBuilder sb = new StringBuilder();
- for (ProductArtifact artifact : testBundleExcludes) {
- log.info("excluding artifact from test jar: " + artifact.getArtifactId());
- sb.append(",").append(artifact.getArtifactId());
- }
- String customExcludes = sb.toString();
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy-dependencies"),
- configuration(
- // Here, runtime means only compile- and runtime-scoped dependencies
- element(name("includeScope"), "runtime"),
- element(name("includeTypes"), "jar"),
- element(name("excludeArtifactIds"), "junit" + customExcludes),
- element(name("outputDirectory"), "${project.build.testOutputDirectory}/META-INF/lib")
- ),
- executionEnvironment()
- );
- }
- private void extractDependencies(final Xpp3Dom configuration) throws MojoExecutionException {
- // Save a copy of the given config (it's mutated when we do the first extraction)
- final Xpp3Dom copyOfConfiguration = new Xpp3Dom(configuration);
- // Do the extraction they asked for ...
- doExtractDependencies(configuration);
- // ... but check whether that caused any files to be overwritten
- warnAboutOverwrites(copyOfConfiguration);
- }
- private void warnAboutOverwrites(final Xpp3Dom configuration) throws MojoExecutionException {
- final Path tempDirectory = createTempDirectoryForOverwriteDetection();
- configuration.getChild("outputDirectory").setValue(tempDirectory.toString());
- configuration.addChild(element("useSubDirectoryPerArtifact", "true").toDom());
- // We set these overWrite flags so that Maven will allow each dependency to be unpacked again (see MDEP-586)
- configuration.addChild(element("overWriteReleases", "true").toDom());
- configuration.addChild(element("overWriteSnapshots", "true").toDom());
- configuration.addChild(element("silent", "true").toDom());
- doExtractDependencies(configuration);
- checkForOverwrites(tempDirectory);
- try {
- deleteDirectory(tempDirectory.toFile());
- } catch (final IOException ignored) {
- // Ignore; it's in the temp folder anyway
- }
- }
- private void checkForOverwrites(final Path dependencyDirectory) {
- try (final Stream<Path> fileStream = walk(dependencyDirectory)) {
- // Map all dependency files to the artifacts that contain them
- final Map<Path, Set<Path>> artifactsByPath = fileStream
- .filter(Files::isRegularFile)
- .map(dependencyDirectory::relativize)
- .collect(groupingBy(MavenGoals::tail, mapping(MavenGoals::head, toCollection(TreeSet::new))));
- // Find any clashes
- final Map<Path, Set<Path>> clashes = artifactsByPath.entrySet().stream()
- .filter(e -> e.getValue().size() > 1)
- .collect(toMap(Entry::getKey, Entry::getValue));
- if (!clashes.isEmpty()) {
- logWarnings(clashes);
- }
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- private void logWarnings(final Map<Path, Set<Path>> clashes) {
- log.warn("Extracting your plugin's dependencies caused the following file(s) to overwrite each other:");
- clashes.entrySet().stream()
- .sorted(comparingByKey())
- .forEach(e -> log.warn(format("-- %s from %s", e.getKey(), e.getValue())));
- log.warn("To prevent this, set <extractDependencies> to false in your AMPS configuration");
- }
- private static Path head(final Path path) {
- return path.subpath(0, 1);
- }
- private static Path tail(final Path path) {
- return path.subpath(1, path.getNameCount());
- }
- private Path createTempDirectoryForOverwriteDetection() {
- final Path targetDirectory = Paths.get(ctx.getProject().getBuild().getDirectory());
- try {
- createDirectories(targetDirectory);
- return createTempDirectory(targetDirectory, "amps-overwrite-detection-");
- } catch (final IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- private void doExtractDependencies(final Xpp3Dom configuration) throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("unpack-dependencies"),
- configuration,
- executionEnvironment()
- );
- }
- public void extractBundledDependencies() throws MojoExecutionException {
- extractDependencies(configuration(
- // Here, runtime means only compile- and runtime-scoped dependencies
- element(name("includeScope"), "runtime"),
- element(name("includeTypes"), "jar"),
- element(name("excludes"), "atlassian-plugin.xml, module-info.class, META-INF/MANIFEST.MF, META-INF/*.DSA, META-INF/*.SF"),
- element(name("outputDirectory"), "${project.build.outputDirectory}")
- ));
- }
- public void extractTestBundledDependenciesExcludingTestScope(List<ProductArtifact> testBundleExcludes)
- throws MojoExecutionException {
- StringBuilder sb = new StringBuilder();
- for (ProductArtifact artifact : testBundleExcludes) {
- sb.append(",").append(artifact.getArtifactId());
- }
- String customExcludes = sb.toString();
- extractDependencies(configuration(
- // Here, runtime means only compile- and runtime-scoped dependencies
- element(name("includeScope"), "runtime"),
- element(name("includeTypes"), "jar"),
- element(name("excludeArtifactIds"), "junit" + customExcludes),
- element(name("excludes"), "atlassian-plugin.xml, module-info.class, META-INF/MANIFEST.MF, META-INF/*.DSA, META-INF/*.SF"),
- element(name("outputDirectory"), "${project.build.testOutputDirectory}")
- ));
- }
- public void extractTestBundledDependencies(List<ProductArtifact> testBundleExcludes) throws MojoExecutionException {
- StringBuilder sb = new StringBuilder();
- for (ProductArtifact artifact : testBundleExcludes) {
- sb.append(",").append(artifact.getArtifactId());
- }
- String customExcludes = sb.toString();
- extractDependencies(configuration(
- element(name("excludeArtifactIds"), "junit" + customExcludes),
- element(name("includeTypes"), "jar"),
- element(name("useSubDirectoryPerScope"), "true"),
- element(name("excludes"), "atlassian-plugin.xml, module-info.class, META-INF/MANIFEST.MF, META-INF/*.DSA, META-INF/*.SF"),
- element(name("outputDirectory"), "${project.build.directory}/testlibs")
- ));
- File targetDir = new File(ctx.getProject().getBuild().getDirectory());
- File testlibsDir = new File(targetDir, "testlibs");
- File compileLibs = new File(testlibsDir, "compile");
- File testLibs = new File(testlibsDir, "test");
- File testClassesDir = new File(ctx.getProject().getBuild().getTestOutputDirectory());
- try {
- compileLibs.mkdirs();
- testLibs.mkdirs();
- FileUtils.copyDirectory(compileLibs, testClassesDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("META-INF")));
- FileUtils.copyDirectory(testLibs, testClassesDir, FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("META-INF")));
- } catch (IOException e) {
- throw new MojoExecutionException("unable to copy test libs", e);
- }
- }
- public void compressResources(boolean compressJs, boolean compressCss, boolean useClosureForJs, Charset cs, Map<String, String> closureOptions) throws MojoExecutionException {
- MinifierParameters minifierParameters = new MinifierParameters(compressJs,
- compressCss,
- useClosureForJs,
- cs,
- log,
- closureOptions
- );
- new ResourcesMinifier(minifierParameters).minify(ctx.getProject().getBuild().getResources(), ctx.getProject().getBuild().getOutputDirectory());
- }
- public void filterPluginDescriptor() throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-resources-plugin"),
- goal("copy-resources"),
- configuration(
- element(name("encoding"), "UTF-8"),
- element(name("resources"),
- element(name("resource"),
- element(name("directory"), "src/main/resources"),
- element(name("filtering"), "true"),
- element(name("includes"),
- element(name("include"), "atlassian-plugin.xml"))
- )
- ),
- element(name("outputDirectory"), "${project.build.outputDirectory}")
- ),
- executionEnvironment()
- );
- XmlCompressor compressor = new XmlCompressor();
- File pluginXmlFile = new File(ctx.getProject().getBuild().getOutputDirectory(), "atlassian-plugin.xml");
- if (pluginXmlFile.exists()) {
- try {
- String source = FileUtils.readFileToString(pluginXmlFile, UTF_8);
- String min = compressor.compress(source);
- FileUtils.writeStringToFile(pluginXmlFile, min, UTF_8);
- } catch (IOException e) {
- throw new MojoExecutionException("IOException while minifying plugin XML file", e);
- }
- }
- }
- public void filterTestPluginDescriptor() throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-resources-plugin"),
- goal("copy-resources"),
- configuration(
- element(name("encoding"), "UTF-8"),
- element(name("resources"),
- element(name("resource"),
- element(name("directory"), "src/test/resources"),
- element(name("filtering"), "true"),
- element(name("includes"),
- element(name("include"), "atlassian-plugin.xml"))
- )
- ),
- element(name("outputDirectory"), "${project.build.testOutputDirectory}")
- ),
- executionEnvironment()
- );
- }
- /**
- * Runs the current project's unit tests via the Surefire plugin. Excludes tests whose names match the pattern
- * {@value #REGEX_INTEGRATION_TESTS} and inner classes.
- *
- * @param systemProperties see SureFire's systemPropertyVariables parameter
- * @param excludedGroups see SureFire's excludedGroups parameter
- * @param category see SureFire's category parameter
- * @throws MojoExecutionException if something goes wrong
- */
- public void runUnitTests(
- final Map<String, Object> systemProperties, final String excludedGroups, final String category)
- throws MojoExecutionException {
- final Element systemProps = convertPropsToElements(systemProperties);
- final Xpp3Dom config = configuration(
- systemProps,
- element(name("excludes"),
- element(name("exclude"), REGEX_INTEGRATION_TESTS),
- element(name("exclude"), "**/*$*")),
- element(name("excludedGroups"), excludedGroups)
- );
- if (isNotBlank(category)) {
- config.addChild(groupsElement(category));
- }
- final Plugin surefirePlugin =
- ctx.getPlugin("org.apache.maven.plugins", "maven-surefire-plugin");
- log.info("Surefire " + surefirePlugin.getVersion() + " test configuration:");
- log.info(config.toString());
- mojoExecutorWrapper.executeWithMergedConfig(
- surefirePlugin,
- goal("test"),
- config,
- executionEnvironment()
- );
- }
- /**
- * Copies the given WAR artifact (typically the webapp for a product) to the given target directory, as
- * {@code <productId>-original.war}.
- *
- * @param artifact the WAR artifact to copy
- * @param targetDirectory the destination directory
- * @param productId the AMPS product ID to use as the base name of the destination file
- * @return the destination file
- * @throws MojoExecutionException if execution fails
- */
- @Nonnull
- public File copyWebappWar(final ProductArtifact artifact, final File targetDirectory, final String productId)
- throws MojoExecutionException {
- final File destinationFile = new File(targetDirectory, productId + "-original.war");
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy"),
- configuration(
- element(name("artifactItems"),
- element(name("artifactItem"),
- element(name("groupId"), artifact.getGroupId()),
- element(name("artifactId"), artifact.getArtifactId()),
- element(name("type"), "war"),
- element(name("version"), artifact.getVersion()),
- element(name("destFileName"), destinationFile.getName()))),
- element(name("outputDirectory"), targetDirectory.getPath())
- ),
- executionEnvironment()
- );
- return destinationFile;
- }
- public void unpackWebappWar(final File targetDirectory, final ProductArtifact artifact)
- throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("unpack"),
- configuration(
- element(name("artifactItems"),
- element(name("artifactItem"),
- element(name("groupId"), artifact.getGroupId()),
- element(name("artifactId"), artifact.getArtifactId()),
- element(name("type"), "war"),
- element(name("version"), artifact.getVersion()))),
- element(name("outputDirectory"), targetDirectory.getPath()),
- //Overwrite needs to be enabled or unpack won't unpack multiple copies
- //of the same dependency GAV, _even to different output directories_
- element(name("overWriteReleases"), "true"),
- element(name("overWriteSnapshots"), "true"),
- //Use the JVM's chmod; it's faster than forking
- element(name("useJvmChmod"), "true")
- ),
- executionEnvironment()
- );
- }
- /**
- * Copies {@code artifacts} to the {@code outputDirectory}. Artifacts are looked up in this order:
- *
- * <ol>
- * <li>in the Maven reactor</li>
- * <li>in the Maven repositories</li>
- * </ol>
- * <p>
- * This can't be used in a goal that happens before the <em>package</em> phase, as artifacts in the reactor will not
- * be packaged (and therefore copyable) until this phase.
- *
- * @param outputDirectory the directory to copy artifacts to
- * @param artifacts the list of artifact to copy to the given directory
- */
- public void copyPlugins(final File outputDirectory, final List<ProductArtifact> artifacts)
- throws MojoExecutionException {
- for (ProductArtifact artifact : artifacts) {
- final MavenProject artifactReactorProject = getReactorProjectForArtifact(artifact);
- if (artifactReactorProject != null) {
- log.debug(artifact + " will be copied from reactor project " + artifactReactorProject);
- final File artifactFile = artifactReactorProject.getArtifact().getFile();
- if (artifactFile == null) {
- log.warn("The plugin " + artifact + " is in the reactor but not the file hasn't been attached. Skipping.");
- } else {
- log.debug("Copying " + artifactFile + " to " + outputDirectory);
- try {
- FileUtils.copyFile(artifactFile, new File(outputDirectory, artifactFile.getName()));
- } catch (IOException e) {
- throw new MojoExecutionException("Could not copy " + artifact + " to " + outputDirectory, e);
- }
- }
- } else {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy"),
- configuration(
- element(name("artifactItems"),
- element(name("artifactItem"),
- element(name("groupId"), artifact.getGroupId()),
- element(name("artifactId"), artifact.getArtifactId()),
- element(name("version"), artifact.getVersion()))),
- element(name("outputDirectory"), outputDirectory.getPath())
- ),
- executionEnvironment());
- }
- }
- }
- private MavenProject getReactorProjectForArtifact(ProductArtifact artifact) {
- for (final MavenProject project : ctx.getReactor()) {
- if (project.getGroupId().equals(artifact.getGroupId())
- && project.getArtifactId().equals(artifact.getArtifactId())
- && project.getVersion().equals(artifact.getVersion())) {
- return project;
- }
- }
- return null;
- }
- private Plugin bndPlugin() {
- final Plugin bndPlugin = ctx.getPlugin("org.apache.felix", "maven-bundle-plugin");
- log.info("using maven-bundle-plugin v" + bndPlugin.getVersion());
- return bndPlugin;
- }
- public static String getReportsDirectory(
- final File targetDirectory, final String testGroupId, final String containerId) {
- return targetDirectory.getAbsolutePath() + "/" + testGroupId + "/" + containerId + "/surefire-reports";
- }
- /**
- * Runs the plugin's integration tests by invoking {@code maven-failsafe-plugin:integration-test}.
- *
- * @param reportsDirectory the directory in which to write the test reports
- * @param includes the filename patterns of the tests to be run, e.g. "∗∗/IT∗.java"
- * @param excludes the filename patterns of the tests <em>not</em> to be run
- * @param systemProperties the System properties to pass to the JUnit tests
- * @param category if specified, only classes/methods/etc decorated with one of these groups/categories/tags will be
- * included in test run. In JUnit4, this affects tests annotated with the
- * {@link org.junit.experimental.categories.Category Category} annotation.
- * @param debug see the {@code debugForkedProcess} parameter of the {@code failsafe:integration-test} goal
- * @throws MojoExecutionException if something goes wrong, or any tests fail
- */
- public void runIntegrationTests(
- final String reportsDirectory, final List<String> includes, final List<String> excludes,
- final Map<String, Object> systemProperties, final String category, @Nullable final String debug)
- throws MojoExecutionException {
- final Xpp3Dom integrationTestConfig =
- getIntegrationTestConfig(includes, excludes, systemProperties, category, debug, reportsDirectory);
- final Plugin failsafePlugin =
- ctx.getPlugin("org.apache.maven.plugins", "maven-failsafe-plugin");
- log.info("Failsafe " + failsafePlugin.getVersion() + " integration-test configuration:");
- log.info(integrationTestConfig.toString());
- mojoExecutorWrapper.executeWithMergedConfig(
- failsafePlugin,
- goal("integration-test"),
- integrationTestConfig,
- executionEnvironment()
- );
- }
- /**
- * Runs {@code maven-failsafe-plugin:verify}.
- *
- * @param reportsDirectory the directory containing the test reports
- * @throws MojoExecutionException if something goes wrong or a test failed
- */
- public void runVerify(final String reportsDirectory)
- throws MojoExecutionException {
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-failsafe-plugin"),
- goal("verify"),
- configuration(element(name(REPORTS_DIRECTORY), reportsDirectory)),
- executionEnvironment()
- );
- }
- private Xpp3Dom getIntegrationTestConfig(
- final List<String> includes, final List<String> excludes, final Map<String, Object> systemProperties,
- final String category, final @Nullable String debug, final String reportsDirectory) {
- final Element[] includeElements = includes.stream()
- .map(include -> element(name("include"), include))
- .toArray(Element[]::new);
- systemProperties.put(REPORTS_DIRECTORY, reportsDirectory);
- final Element systemPropsElement = convertPropsToElements(systemProperties);
- final Xpp3Dom config = configuration(
- element(name("includes"), includeElements),
- element(name("excludes"), getTestExcludes(excludes)),
- systemPropsElement,
- element(name(REPORTS_DIRECTORY), reportsDirectory)
- );
- if (debug != null) {
- config.addChild(element(name("debugForkedProcess"), debug).toDom());
- }
- if (isNotBlank(category)) {
- config.addChild(groupsElement(category));
- }
- return config;
- }
- private Element[] getTestExcludes(final List<String> userSpecifiedExcludes) {
- final Set<String> allExcludes = new HashSet<>(userSpecifiedExcludes);
- allExcludes.add(ABSTRACT_CLASSES);
- allExcludes.add(INNER_CLASSES);
- return allExcludes.stream()
- .map(exclude -> element(name("exclude"), exclude))
- .toArray(Element[]::new);
- }
- private Xpp3Dom groupsElement(final String category) {
- return element(name("groups"), category).toDom();
- }
- /**
- * Converts a map of System properties to a Maven {@code systemPropertyVariables} config element.
- */
- private Element convertPropsToElements(final Map<String, Object> systemProperties) {
- final List<Element> properties = new ArrayList<>();
- for (Entry<String, Object> entry : systemProperties.entrySet()) {
- log.info("adding system property to configuration: " + entry.getKey() + "::" + entry.getValue());
- properties.add(element(name(entry.getKey()), entry.getValue().toString()));
- }
- return element(name("systemPropertyVariables"), properties.toArray(new Element[0]));
- }
- /**
- * Installs a P2 plugin into a running Atlassian product.
- *
- * @param pdkParams describes the intended host product and the plugin to be uploaded
- * @throws MojoExecutionException if the installation fails
- */
- public void installPlugin(final PdkParams pdkParams) throws MojoExecutionException {
- final String baseUrl = getBaseUrl(pdkParams.getServer(), pdkParams.getPort(), pdkParams.getContextPath());
- // We delegate the plugin installation to https://bitbucket.org/atlassian/atlassian-pdk-maven-plugin
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("com.atlassian.maven.plugins", "atlassian-pdk"),
- goal("install"),
- configuration(
- element(name("pluginFile"), pdkParams.getPluginFile()),
- element(name("username"), pdkParams.getUsername()),
- element(name("password"), pdkParams.getPassword()),
- element(name("serverUrl"), baseUrl),
- element(name("pluginKey"), pdkParams.getPluginKey())
- ),
- executionEnvironment()
- );
- }
- public void installIdeaPlugin() throws MojoExecutionException {
- // See https://github.com/atlassian/maven-cli-plugin/blob/master/maven/src/main/java/org/twdata/maven/cli/IdeaMojo.java
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.twdata.maven", "maven-cli-plugin"),
- goal("idea"),
- configuration(),
- executionEnvironment()
- );
- }
- /**
- * Downloads the ZIP version of the specified artifact into the given directory.
- *
- * @param targetDirectory the destination directory
- * @param artifact the artifact to copy (as {@code <type>zip</type>})
- * @param localName the name to give the copy
- * @return the destination ZIP file
- * @throws MojoExecutionException if execution fails
- */
- public File copyZip(final File targetDirectory, final ProductArtifact artifact, final String localName)
- throws MojoExecutionException {
- final File artifactZip = new File(targetDirectory, localName);
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-dependency-plugin"),
- goal("copy"),
- configuration(
- element(name("artifactItems"),
- element(name("artifactItem"),
- element(name("groupId"), artifact.getGroupId()),
- element(name("artifactId"), artifact.getArtifactId()),
- element(name("type"), "zip"),
- element(name("version"), artifact.getVersion()),
- element(name("destFileName"), artifactZip.getName()))),
- element(name("outputDirectory"), artifactZip.getParent())
- ),
- executionEnvironment()
- );
- return artifactZip;
- }
- public void generateBundleManifest(final Map<String, String> instructions, final Map<String, String> basicAttributes) throws MojoExecutionException {
- final List<Element> instlist = new ArrayList<>();
- for (final Entry<String, String> entry : instructions.entrySet()) {
- instlist.add(element(entry.getKey(), entry.getValue()));
- }
- if (!instructions.containsKey(Constants.IMPORT_PACKAGE)) {
- instlist.add(element(Constants.IMPORT_PACKAGE, "*;resolution:=optional"));
- // BND will expand the wildcard to a list of actually-used packages, but this tells it to mark
- // them all as optional
- }
- for (final Entry<String, String> entry : basicAttributes.entrySet()) {
- instlist.add(element(entry.getKey(), entry.getValue()));
- }
- mojoExecutorWrapper.executeWithMergedConfig(
- bndPlugin(),
- goal("manifest"),
- configuration(
- element(name("supportedProjectTypes"),
- element(name("supportedProjectType"), "jar"),
- element(name("supportedProjectType"), "bundle"),
- element(name("supportedProjectType"), "war"),
- element(name("supportedProjectType"), "atlassian-plugin")),
- element(name("instructions"), instlist.toArray(new Element[0]))
- ),
- executionEnvironment()
- );
- }
- public void generateTestBundleManifest(final Map<String, String> instructions, final Map<String, String> basicAttributes) throws MojoExecutionException {
- final List<Element> instlist = new ArrayList<>();
- for (final Entry<String, String> entry : instructions.entrySet()) {
- instlist.add(element(entry.getKey(), entry.getValue()));
- }
- if (!instructions.containsKey(Constants.IMPORT_PACKAGE)) {
- instlist.add(element(Constants.IMPORT_PACKAGE, "*;resolution:=optional"));
- // BND will expand the wildcard to a list of actually-used packages, but this tells it to mark
- // them all as optional
- }
- for (final Entry<String, String> entry : basicAttributes.entrySet()) {
- instlist.add(element(entry.getKey(), entry.getValue()));
- }
- mojoExecutorWrapper.executeWithMergedConfig(
- bndPlugin(),
- goal("manifest"),
- configuration(
- element(name("manifestLocation"), "${project.build.testOutputDirectory}/META-INF"),
- element(name("supportedProjectTypes"),
- element(name("supportedProjectType"), "jar"),
- element(name("supportedProjectType"), "bundle"),
- element(name("supportedProjectType"), "war"),
- element(name("supportedProjectType"), "atlassian-plugin")),
- element(name("instructions"), instlist.toArray(new Element[0]))
- ),
- executionEnvironment()
- );
- }
- public void generateMinimalManifest(final Map<String, String> basicAttributes) throws MojoExecutionException {
- File metaInf = file(ctx.getProject().getBuild().getOutputDirectory(), "META-INF");
- if (!metaInf.exists()) {
- metaInf.mkdirs();
- }
- File mf = file(ctx.getProject().getBuild().getOutputDirectory(), "META-INF", "MANIFEST.MF");
- Manifest m = new Manifest();
- m.getMainAttributes().putValue("Manifest-Version", "1.0");
- for (Entry<String, String> entry : basicAttributes.entrySet()) {
- m.getMainAttributes().putValue(entry.getKey(), entry.getValue());
- }
- try (FileOutputStream fos = new FileOutputStream(mf)) {
- m.write(fos);
- } catch (IOException e) {
- throw new MojoExecutionException("Unable to create manifest", e);
- }
- }
- public void generateTestMinimalManifest(final Map<String, String> basicAttributes) throws MojoExecutionException {
- File metaInf = file(ctx.getProject().getBuild().getTestOutputDirectory(), "META-INF");
- if (!metaInf.exists()) {
- metaInf.mkdirs();
- }
- File mf = file(ctx.getProject().getBuild().getTestOutputDirectory(), "META-INF", "MANIFEST.MF");
- Manifest m = new Manifest();
- m.getMainAttributes().putValue("Manifest-Version", "1.0");
- for (Entry<String, String> entry : basicAttributes.entrySet()) {
- m.getMainAttributes().putValue(entry.getKey(), entry.getValue());
- }
- try (FileOutputStream fos = new FileOutputStream(mf)) {
- m.write(fos);
- } catch (IOException e) {
- throw new MojoExecutionException("Unable to create manifest", e);
- }
- }
- /**
- * JARs up the current project.
- *
- * @param manifestExists whether there is a {@code target/classes/META-INF/MANIFEST.MF} file to include in the JAR.
- * @throws MojoExecutionException if the operation fails
- */
- public void jarWithOptionalManifest(final boolean manifestExists) throws MojoExecutionException {
- Element[] archive = new Element[0];
- if (manifestExists) {
- archive = new Element[]{element(name("manifestFile"), "${project.build.outputDirectory}/META-INF/MANIFEST.MF")};
- }
- mojoExecutorWrapper.executeWithMergedConfig(
- ctx.getPlugin("org.apache.maven.plugins", "maven-jar-plugin"),
- goal("jar"),
- configuration(
- element(n