[MINVOKER-218] Change package to o.a.m.plugins git-svn-id: https://svn.apache.org/repos/asf/maven/plugins/trunk@1795243 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/it/local-repo-url/src/it/project/postbuild.groovy b/src/it/local-repo-url/src/it/project/postbuild.groovy index 08e0795..6b8ec74 100644 --- a/src/it/local-repo-url/src/it/project/postbuild.groovy +++ b/src/it/local-repo-url/src/it/project/postbuild.groovy
@@ -24,7 +24,7 @@ assert interpolatedSettings.isFile() def filename = new File( basedir, "../../../../../local-repo" ).canonicalPath -// Convert URL, see org.apache.maven.plugin.invoker.AbstractInvokerMojo.toUrl(String) +// Convert URL, see org.apache.maven.plugins.invoker.AbstractInvokerMojo.toUrl(String) String url = "file://" + new File( filename ).toURI().path if ( url.endsWith( "/" ) ) {
diff --git a/src/main/java/org/apache/maven/plugin/invoker/AbstractInvokerMojo.java b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java similarity index 99% rename from src/main/java/org/apache/maven/plugin/invoker/AbstractInvokerMojo.java rename to src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java index dfe85fe..bdd1416 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/AbstractInvokerMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
@@ -1,2553 +1,2553 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.Reader; -import java.io.Writer; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; -import java.util.StringTokenizer; -import java.util.TreeSet; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.model.Model; -import org.apache.maven.model.Profile; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecution; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.invoker.model.BuildJob; -import org.apache.maven.plugin.invoker.model.io.xpp3.BuildJobXpp3Writer; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apache.maven.settings.Settings; -import org.apache.maven.settings.SettingsUtils; -import org.apache.maven.settings.TrackableBase; -import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; -import org.apache.maven.settings.building.SettingsBuilder; -import org.apache.maven.settings.building.SettingsBuildingException; -import org.apache.maven.settings.building.SettingsBuildingRequest; -import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer; -import org.apache.maven.shared.invoker.CommandLineConfigurationException; -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.InvocationResult; -import org.apache.maven.shared.invoker.Invoker; -import org.apache.maven.shared.invoker.MavenCommandLineBuilder; -import org.apache.maven.shared.invoker.MavenInvocationException; -import org.apache.maven.shared.scriptinterpreter.RunErrorException; -import org.apache.maven.shared.scriptinterpreter.RunFailureException; -import org.apache.maven.shared.scriptinterpreter.ScriptRunner; -import org.apache.maven.shared.utils.logging.MessageBuilder; -import org.codehaus.plexus.interpolation.InterpolationException; -import org.codehaus.plexus.interpolation.Interpolator; -import org.codehaus.plexus.interpolation.MapBasedValueSource; -import org.codehaus.plexus.interpolation.RegexBasedInterpolator; -import org.codehaus.plexus.util.DirectoryScanner; -import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.IOUtil; -import org.codehaus.plexus.util.InterpolationFilterReader; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.ReflectionUtils; -import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.WriterFactory; -import org.codehaus.plexus.util.cli.CommandLineException; -import org.codehaus.plexus.util.cli.CommandLineUtils; -import org.codehaus.plexus.util.cli.Commandline; -import org.codehaus.plexus.util.cli.StreamConsumer; -import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; - -/** - * Provides common code for mojos invoking sub builds. - * - * @author Stephen Connolly - * @since 15-Aug-2009 09:09:29 - */ -public abstract class AbstractInvokerMojo - extends AbstractMojo -{ - /** - * The zero-based column index where to print the invoker result. - */ - private static final int RESULT_COLUMN = 60; - - /** - * Flag used to suppress certain invocations. This is useful in tailoring the build using profiles. - * - * @since 1.1 - */ - @Parameter( property = "invoker.skip", defaultValue = "false" ) - private boolean skipInvocation; - - /** - * Flag used to suppress the summary output notifying of successes and failures. If set to <code>true</code>, the - * only indication of the build's success or failure will be the effect it has on the main build (if it fails, the - * main build should fail as well). If {@link #streamLogs} is enabled, the sub-build summary will also provide an - * indication. - */ - @Parameter( defaultValue = "false" ) - protected boolean suppressSummaries; - - /** - * Flag used to determine whether the build logs should be output to the normal mojo log. - */ - @Parameter( property = "invoker.streamLogs", defaultValue = "false" ) - private boolean streamLogs; - - /** - * The local repository for caching artifacts. It is strongly recommended to specify a path to an isolated - * repository like <code>${project.build.directory}/it-repo</code>. Otherwise, your ordinary local repository will - * be used, potentially soiling it with broken artifacts. - */ - @Parameter( property = "invoker.localRepositoryPath", defaultValue = "${settings.localRepository}" ) - private File localRepositoryPath; - - /** - * Directory to search for integration tests. - */ - @Parameter( property = "invoker.projectsDirectory", defaultValue = "${basedir}/src/it/" ) - private File projectsDirectory; - - /** - * Base directory where all build reports are written to. Every execution of an integration test will produce an XML - * file which contains the information about success or failure of that particular build job. The format of the - * resulting XML file is documented in the given <a href="./build-job.html">build-job</a> reference. - * - * @since 1.4 - */ - @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" ) - private File reportsDirectory; - - /** - * A flag to disable the generation of build reports. - * - * @since 1.4 - */ - @Parameter( property = "invoker.disableReports", defaultValue = "false" ) - private boolean disableReports; - - /** - * Directory to which projects should be cloned prior to execution. If not specified, each integration test will be - * run in the directory in which the corresponding IT POM was found. In this case, you most likely want to configure - * your SCM to ignore <code>target</code> and <code>build.log</code> in the test's base directory. - * - * @since 1.1 - */ - @Parameter - private File cloneProjectsTo; - - // CHECKSTYLE_OFF: LineLength - /** - * Some files are normally excluded when copying the IT projects from the directory specified by the parameter - * projectsDirectory to the directory given by cloneProjectsTo (e.g. <code>.svn</code>, <code>CVS</code>, - * <code>*~</code>, etc: see <a href= - * "https://codehaus-plexus.github.io/plexus-utils/apidocs/org/codehaus/plexus/util/AbstractScanner.html#DEFAULTEXCLUDES"> - * reference</a> for full list). Setting this parameter to <code>true</code> will cause all files to be copied to - * the <code>cloneProjectsTo</code> directory. - * - * @since 1.2 - */ - @Parameter( defaultValue = "false" ) - private boolean cloneAllFiles; - // CHECKSTYLE_ON: LineLength - - /** - * Ensure the {@link #cloneProjectsTo} directory is not polluted with files from earlier invoker runs. - * - * @since 1.6 - */ - @Parameter( defaultValue = "false" ) - private boolean cloneClean; - - /** - * A single POM to build, skipping any scanning parameters and behavior. - */ - @Parameter( property = "invoker.pom" ) - private File pom; - - /** - * Include patterns for searching the integration test directory for projects. This parameter is meant to be set - * from the POM. If this parameter is not set, the plugin will search for all <code>pom.xml</code> files one - * directory below {@link #projectsDirectory} (i.e. <code>*/pom.xml</code>).<br> - * <br> - * Starting with version 1.3, mere directories can also be matched by these patterns. For example, the include - * pattern <code>*</code> will run Maven builds on all immediate sub directories of {@link #projectsDirectory}, - * regardless if they contain a <code>pom.xml</code>. This allows to perform builds that need/should not depend on - * the existence of a POM. - */ - @Parameter - private List<String> pomIncludes = Collections.singletonList( "*/pom.xml" ); - - /** - * Exclude patterns for searching the integration test directory. This parameter is meant to be set from the POM. By - * default, no POM files are excluded. For the convenience of using an include pattern like <code>*</code>, the - * custom settings file specified by the parameter {@link #settingsFile} will always be excluded automatically. - */ - @Parameter - private List<String> pomExcludes = Collections.emptyList(); - - /** - * Include patterns for searching the projects directory for projects that need to be run before the other projects. - * This parameter allows to declare projects that perform setup tasks like installing utility artifacts into the - * local repository. Projects matched by these patterns are implicitly excluded from the scan for ordinary projects. - * Also, the exclusions defined by the parameter {@link #pomExcludes} apply to the setup projects, too. Default - * value is: <code>setup*/pom.xml</code>. - * - * @since 1.3 - */ - @Parameter - private List<String> setupIncludes = Collections.singletonList( "setup*/pom.xml" ); - - /** - * The list of goals to execute on each project. Default value is: <code>package</code>. - */ - @Parameter - private List<String> goals = Collections.singletonList( "package" ); - - /** - */ - @Component - private Invoker invoker; - - @Component - private SettingsBuilder settingsBuilder; - - /** - * Relative path of a selector script to run prior in order to decide if the build should be executed. This script - * may be written with either BeanShell or Groovy. If the file extension is omitted (e.g. <code>selector</code>), - * the plugin searches for the file by trying out the well-known extensions <code>.bsh</code> and - * <code>.groovy</code>. If this script exists for a particular project but returns any non-null value different - * from <code>true</code>, the corresponding build is flagged as skipped. In this case, none of the pre-build hook - * script, Maven nor the post-build hook script will be invoked. If this script throws an exception, the - * corresponding build is flagged as in error, and none of the pre-build hook script, Maven not the post-build hook - * script will be invoked. - * - * @since 1.5 - */ - @Parameter( property = "invoker.selectorScript", defaultValue = "selector" ) - private String selectorScript; - - /** - * Relative path of a pre-build hook script to run prior to executing the build. This script may be written with - * either BeanShell or Groovy (since 1.3). If the file extension is omitted (e.g. <code>prebuild</code>), the plugin - * searches for the file by trying out the well-known extensions <code>.bsh</code> and <code>.groovy</code>. If this - * script exists for a particular project but returns any non-null value different from <code>true</code> or throws - * an exception, the corresponding build is flagged as a failure. In this case, neither Maven nor the post-build - * hook script will be invoked. - */ - @Parameter( property = "invoker.preBuildHookScript", defaultValue = "prebuild" ) - private String preBuildHookScript; - - /** - * Relative path of a cleanup/verification hook script to run after executing the build. This script may be written - * with either BeanShell or Groovy (since 1.3). If the file extension is omitted (e.g. <code>verify</code>), the - * plugin searches for the file by trying out the well-known extensions <code>.bsh</code> and <code>.groovy</code>. - * If this script exists for a particular project but returns any non-null value different from <code>true</code> or - * throws an exception, the corresponding build is flagged as a failure. - */ - @Parameter( property = "invoker.postBuildHookScript", defaultValue = "postbuild" ) - private String postBuildHookScript; - - /** - * Location of a properties file that defines CLI properties for the test. - */ - @Parameter( property = "invoker.testPropertiesFile", defaultValue = "test.properties" ) - private String testPropertiesFile; - - /** - * Common set of properties to pass in on each project's command line, via -D parameters. - * - * @since 1.1 - */ - @Parameter - private Map<String, String> properties; - - /** - * Whether to show errors in the build output. - */ - @Parameter( property = "invoker.showErrors", defaultValue = "false" ) - private boolean showErrors; - - /** - * Whether to show debug statements in the build output. - */ - @Parameter( property = "invoker.debug", defaultValue = "false" ) - private boolean debug; - - /** - * Suppress logging to the <code>build.log</code> file. - */ - @Parameter( property = "invoker.noLog", defaultValue = "false" ) - private boolean noLog; - - /** - * List of profile identifiers to explicitly trigger in the build. - * - * @since 1.1 - */ - @Parameter - private List<String> profiles; - - /** - * A list of additional properties which will be used to filter tokens in POMs and goal files. - * - * @since 1.3 - */ - @Parameter - private Map<String, String> filterProperties; - - /** - * The Maven Project Object - * - * @since 1.1 - */ - @Parameter( defaultValue = "${project}", readonly = true, required = true ) - private MavenProject project; - - @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true ) - private MojoExecution mojoExecution; - - /** - * A comma separated list of projectname patterns to run. Specify this parameter to run individual tests by file - * name, overriding the {@link #setupIncludes}, {@link #pomIncludes} and {@link #pomExcludes} parameters. Each - * pattern you specify here will be used to create an include/exclude pattern formatted like - * <code>${projectsDirectory}/<i>pattern</i></code>. To exclude a test, prefix the pattern with a '<code>!</code>'. - * So you can just type <nobr><code>-Dinvoker.test=SimpleTest,Comp*Test,!Compare*</code></nobr> to run builds in - * <code>${projectsDirectory}/SimpleTest</code> and <code>${projectsDirectory}/ComplexTest</code>, but not - * <code>${projectsDirectory}/CompareTest</code> - * - * @since 1.1 (exclusion since 1.8) - */ - @Parameter( property = "invoker.test" ) - private String invokerTest; - - /** - * Path to an alternate <code>settings.xml</code> to use for Maven invocation with all ITs. Note that the - * <code><localRepository></code> element of this settings file is always ignored, i.e. the path given by the - * parameter {@link #localRepositoryPath} is dominant. - * - * @since 1.2 - */ - @Parameter( property = "invoker.settingsFile" ) - private File settingsFile; - - /** - * The <code>MAVEN_OPTS</code> environment variable to use when invoking Maven. This value can be overridden for - * individual integration tests by using {@link #invokerPropertiesFile}. - * - * @since 1.2 - */ - @Parameter( property = "invoker.mavenOpts" ) - private String mavenOpts; - - /** - * The home directory of the Maven installation to use for the forked builds. Defaults to the current Maven - * installation. - * - * @since 1.3 - */ - @Parameter( property = "invoker.mavenHome" ) - private File mavenHome; - - /** - * mavenExecutable can either be a file relative to <code>${maven.home}/bin/</code> or an absolute file. - * - * @since 1.8 - * @see Invoker#setMavenExecutable(File) - */ - @Parameter( property = "invoker.mavenExecutable" ) - private String mavenExecutable; - - /** - * The <code>JAVA_HOME</code> environment variable to use for forked Maven invocations. Defaults to the current Java - * home directory. - * - * @since 1.3 - */ - @Parameter( property = "invoker.javaHome" ) - private File javaHome; - - /** - * The file encoding for the pre-/post-build scripts and the list files for goals and profiles. - * - * @since 1.2 - */ - @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) - private String encoding; - - /** - * The current user system settings for use in Maven. - * - * @since 1.2 - */ - @Parameter( defaultValue = "${settings}", readonly = true, required = true ) - private Settings settings; - - /** - * A flag whether the test class path of the project under test should be included in the class path of the - * pre-/post-build scripts. If set to <code>false</code>, the class path of script interpreter consists only of the - * <a href="dependencies.html">runtime dependencies</a> of the Maven Invoker Plugin. If set the <code>true</code>, - * the project's test class path will be prepended to the interpreter class path. Among others, this feature allows - * the scripts to access utility classes from the test sources of your project. - * - * @since 1.2 - */ - @Parameter( property = "invoker.addTestClassPath", defaultValue = "false" ) - private boolean addTestClassPath; - - /** - * The test class path of the project under test. - */ - @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true ) - private List<String> testClassPath; - - /** - * The name of an optional project-specific file that contains properties used to specify settings for an individual - * Maven invocation. Any property present in the file will override the corresponding setting from the plugin - * configuration. The values of the properties are filtered and may use expressions like - * <code>${project.version}</code> to reference project properties or values from the parameter - * {@link #filterProperties}. The snippet below describes the supported properties: - * <p/> - * - * <pre> - * # A comma or space separated list of goals/phases to execute, may - * # specify an empty list to execute the default goal of the IT project. - * # Environment variables used by maven plugins can be added here - * invoker.goals = clean install -Dplugin.variable=value - * - * # Or you can give things like this if you need. - * invoker.goals = -T2 clean verify - * - * # Optionally, a list of goals to run during further invocations of Maven - * invoker.goals.2 = ${project.groupId}:${project.artifactId}:${project.version}:run - * - * # A comma or space separated list of profiles to activate - * invoker.profiles = its,jdk15 - * - * # The path to an alternative POM or base directory to invoke Maven on, defaults to the - * # project that was originally specified in the plugin configuration - * # Since plugin version 1.4 - * invoker.project = sub-module - * - * # The value for the environment variable MAVEN_OPTS - * invoker.mavenOpts = -Dfile.encoding=UTF-16 -Xms32m -Xmx256m - * - * # Possible values are "fail-fast" (default), "fail-at-end" and "fail-never" - * invoker.failureBehavior = fail-never - * - * # The expected result of the build, possible values are "success" (default) and "failure" - * invoker.buildResult = failure - * - * # A boolean value controlling the aggregator mode of Maven, defaults to "false" - * invoker.nonRecursive = true - * - * # A boolean value controlling the network behavior of Maven, defaults to "false" - * # Since plugin version 1.4 - * invoker.offline = true - * - * # The path to the properties file from which to load system properties, defaults to the - * # filename given by the plugin parameter testPropertiesFile - * # Since plugin version 1.4 - * invoker.systemPropertiesFile = test.properties - * - * # An optional human friendly name for this build job to be included in the build reports. - * # Since plugin version 1.4 - * invoker.name = Test Build 01 - * - * # An optional description for this build job to be included in the build reports. - * # Since plugin version 1.4 - * invoker.description = Checks the support for build reports. - * - * # A comma separated list of JRE versions on which this build job should be run. - * # Since plugin version 1.4 - * invoker.java.version = 1.4+, !1.4.1, 1.7- - * - * # A comma separated list of OS families on which this build job should be run. - * # Since plugin version 1.4 - * invoker.os.family = !windows, unix, mac - * - * # A comma separated list of Maven versions on which this build should be run. - * # Since plugin version 1.5 - * invoker.maven.version = 2.0.10+, !2.1.0, !2.2.0 - * - * # For java.version, maven.version and os.family it is possible to define multiple selectors. - * # If one of the indexed selectors matches, the test is executed. - * # With the invoker.x.y equivalents you can specify global matchers. - * selector.1.java.version = 1.8+ - * selector.1.maven.version = 3.2.5+ - * selector.1.os.family = !windows - * selector.2.maven.version = 3.0+ - * selector.3.java.version = 9+ - * - * # A boolean value controlling the debug logging level of Maven, , defaults to "false" - * # Since plugin version 1.8 - * invoker.debug = true - * </pre> - * - * @since 1.2 - */ - @Parameter( property = "invoker.invokerPropertiesFile", defaultValue = "invoker.properties" ) - private String invokerPropertiesFile; - - /** - * flag to enable show mvn version used for running its (cli option : -V,--show-version ) - * - * @since 1.4 - */ - @Parameter( property = "invoker.showVersion", defaultValue = "false" ) - private boolean showVersion; - - /** - * number of threads for running tests in parallel. This will be the number of maven forked process in parallel. - * - * @since 1.6 - */ - @Parameter( property = "invoker.parallelThreads", defaultValue = "1" ) - private int parallelThreads; - - /** - * @since 1.6 - */ - @Parameter( property = "plugin.artifacts", required = true, readonly = true ) - private List<Artifact> pluginArtifacts; - - /** - * If enable and if you have a settings file configured for the execution, it will be merged with your user - * settings. - * - * @since 1.6 - */ - @Parameter( property = "invoker.mergeUserSettings", defaultValue = "false" ) - private boolean mergeUserSettings; - - /** - * Additional environment variables to set on the command line. - * - * @since 1.8 - */ - @Parameter - private Map<String, String> environmentVariables; - - /** - * Additional variables for use in the hook scripts. - * - * @since 1.9 - */ - @Parameter - private Map<String, String> scriptVariables; - - /** - * The scripter runner that is responsible to execute hook scripts. - */ - private ScriptRunner scriptRunner; - - /** - * A string used to prefix the file name of the filtered POMs in case the POMs couldn't be filtered in-place (i.e. - * the projects were not cloned to a temporary directory), can be <code>null</code>. This will be set to - * <code>null</code> if the POMs have already been filtered during cloning. - */ - private String filteredPomPrefix = "interpolated-"; - - /** - * The format for elapsed build time. - */ - private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) ); - - /** - * The version of Maven which is used to run the builds - */ - private String actualMavenVersion; - - /** - * Invokes Maven on the configured test projects. - * - * @throws org.apache.maven.plugin.MojoExecutionException If the goal encountered severe errors. - * @throws org.apache.maven.plugin.MojoFailureException If any of the Maven builds failed. - */ - public void execute() - throws MojoExecutionException, MojoFailureException - { - if ( skipInvocation ) - { - getLog().info( "Skipping invocation per configuration." - + " If this is incorrect, ensure the skipInvocation parameter is not set to true." ); - return; - } - - if ( StringUtils.isEmpty( encoding ) ) - { - getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING - + ", i.e. build is platform dependent!" ); - } - - // done it here to prevent issues with concurrent access in case of parallel run - if ( !disableReports ) - { - setupReportsFolder(); - } - - BuildJob[] buildJobs; - if ( pom == null ) - { - try - { - buildJobs = getBuildJobs(); - } - catch ( final IOException e ) - { - throw new MojoExecutionException( "Error retrieving POM list from includes, " - + "excludes, and projects directory. Reason: " + e.getMessage(), e ); - } - } - else - { - try - { - projectsDirectory = pom.getCanonicalFile().getParentFile(); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to discover projectsDirectory from " - + "pom File parameter. Reason: " + e.getMessage(), e ); - } - - buildJobs = new BuildJob[] { new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) }; - } - - if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) ) - { - doFailIfNoProjects(); - - getLog().info( "No projects were selected for execution." ); - return; - } - - handleScriptRunnerWithScriptClassPath(); - - Collection<String> collectedProjects = new LinkedHashSet<String>(); - for ( BuildJob buildJob : buildJobs ) - { - collectProjects( projectsDirectory, buildJob.getProject(), collectedProjects, true ); - } - - File projectsDir = projectsDirectory; - - if ( cloneProjectsTo != null ) - { - cloneProjects( collectedProjects ); - projectsDir = cloneProjectsTo; - } - else - { - getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" ); - } - - // First run setup jobs. - BuildJob[] setupBuildJobs = null; - try - { - setupBuildJobs = getSetupBuildJobsFromFolders(); - } - catch ( IOException e ) - { - getLog().error( "Failure during scanning of folders.", e ); - // TODO: Check shouldn't we fail in case of problems? - } - - if ( setupBuildJobs != null ) - { - // Run setup jobs in single thread - // mode. - // - // Some Idea about ordering? - getLog().info( "Running Setup Jobs" ); - runBuilds( projectsDir, setupBuildJobs, 1 ); - } - - // Afterwards run all other jobs. - BuildJob[] nonSetupBuildJobs = getNonSetupJobs( buildJobs ); - // We will run the non setup jobs with the configured - // parallelThreads number. - runBuilds( projectsDir, nonSetupBuildJobs, parallelThreads ); - - writeSummaryFile( nonSetupBuildJobs ); - - processResults( new InvokerSession( nonSetupBuildJobs ) ); - - } - - /** - * This will create the necessary folders for the reports. - * - * @throws MojoExecutionException in case of failure during creation of the reports folder. - */ - private void setupReportsFolder() - throws MojoExecutionException - { - // If it exists from previous run... - if ( reportsDirectory.exists() ) - { - try - { - FileUtils.deleteDirectory( reportsDirectory ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failure while trying to delete " - + reportsDirectory.getAbsolutePath(), e ); - } - } - if ( !reportsDirectory.mkdirs() ) - { - throw new MojoExecutionException( "Failure while creating the " + reportsDirectory.getAbsolutePath() ); - } - } - - private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs ) - { - List<BuildJob> result = new LinkedList<BuildJob>(); - for ( int i = 0; i < buildJobs.length; i++ ) - { - if ( !buildJobs[i].getType().equals( BuildJob.Type.SETUP ) ) - { - result.add( buildJobs[i] ); - } - } - BuildJob[] buildNonSetupJobs = result.toArray( new BuildJob[result.size()] ); - return buildNonSetupJobs; - } - - private void handleScriptRunnerWithScriptClassPath() - { - final List<String> scriptClassPath; - if ( addTestClassPath ) - { - scriptClassPath = new ArrayList<String>( testClassPath ); - for ( Artifact pluginArtifact : pluginArtifacts ) - { - scriptClassPath.remove( pluginArtifact.getFile().getAbsolutePath() ); - } - } - else - { - scriptClassPath = null; - } - scriptRunner = new ScriptRunner( getLog() ); - scriptRunner.setScriptEncoding( encoding ); - scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath ); - if ( scriptVariables != null ) - { - for ( Entry<String, String> entry : scriptVariables.entrySet() ) - { - scriptRunner.setGlobalVariable( entry.getKey(), entry.getValue() ); - } - } - scriptRunner.setClassPath( scriptClassPath ); - } - - private void writeSummaryFile( BuildJob[] buildJobs ) - throws MojoExecutionException - { - - File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" ); - - try - { - Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ); - - for ( int i = 0; i < buildJobs.length; i++ ) - { - BuildJob buildJob = buildJobs[i]; - if ( !buildJob.getResult().equals( BuildJob.Result.SUCCESS ) ) - { - writer.append( buildJob.getResult() ); - writer.append( " [" ); - writer.append( buildJob.getProject() ); - writer.append( "] " ); - if ( buildJob.getFailureMessage() != null ) - { - writer.append( " " ); - writer.append( buildJob.getFailureMessage() ); - } - writer.append( "\n" ); - } - } - - writer.close(); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to write summary report " + summaryReportFile, e ); - } - } - - protected void doFailIfNoProjects() - throws MojoFailureException - { - // should only be used during run and verify - } - - /** - * Processes the results of invoking the build jobs. - * - * @param invokerSession The session with the build jobs, must not be <code>null</code>. - * @throws MojoFailureException If the mojo had failed as a result of invoking the build jobs. - * @since 1.4 - */ - abstract void processResults( InvokerSession invokerSession ) - throws MojoFailureException; - - /** - * Creates a new reader for the specified file, using the plugin's {@link #encoding} parameter. - * - * @param file The file to create a reader for, must not be <code>null</code>. - * @return The reader for the file, never <code>null</code>. - * @throws java.io.IOException If the specified file was not found or the configured encoding is not supported. - */ - private Reader newReader( File file ) - throws IOException - { - if ( StringUtils.isNotEmpty( encoding ) ) - { - return ReaderFactory.newReader( file, encoding ); - } - else - { - return ReaderFactory.newPlatformReader( file ); - } - } - - /** - * Collects all projects locally reachable from the specified project. The method will as such try to read the POM - * and recursively follow its parent/module elements. - * - * @param projectsDir The base directory of all projects, must not be <code>null</code>. - * @param projectPath The relative path of the current project, can denote either the POM or its base directory, - * must not be <code>null</code>. - * @param projectPaths The set of already collected projects to add new projects to, must not be <code>null</code>. - * This set will hold the relative paths to either a POM file or a project base directory. - * @param included A flag indicating whether the specified project has been explicitly included via the parameter - * {@link #pomIncludes}. Such projects will always be added to the result set even if there is no - * corresponding POM. - * @throws org.apache.maven.plugin.MojoExecutionException If the project tree could not be traversed. - */ - private void collectProjects( File projectsDir, String projectPath, Collection<String> projectPaths, - boolean included ) - throws MojoExecutionException - { - projectPath = projectPath.replace( '\\', '/' ); - File pomFile = new File( projectsDir, projectPath ); - if ( pomFile.isDirectory() ) - { - pomFile = new File( pomFile, "pom.xml" ); - if ( !pomFile.exists() ) - { - if ( included ) - { - projectPaths.add( projectPath ); - } - return; - } - if ( !projectPath.endsWith( "/" ) ) - { - projectPath += '/'; - } - projectPath += "pom.xml"; - } - else if ( !pomFile.isFile() ) - { - return; - } - if ( !projectPaths.add( projectPath ) ) - { - return; - } - getLog().debug( "Collecting parent/child projects of " + projectPath ); - - Model model = PomUtils.loadPom( pomFile ); - - try - { - String projectsRoot = projectsDir.getCanonicalPath(); - String projectDir = pomFile.getParent(); - - String parentPath = "../pom.xml"; - if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) ) - { - parentPath = model.getParent().getRelativePath(); - } - String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot ); - if ( parent != null ) - { - collectProjects( projectsDir, parent, projectPaths, false ); - } - - Collection<String> modulePaths = new LinkedHashSet<String>(); - - modulePaths.addAll( model.getModules() ); - - for ( Profile profile : model.getProfiles() ) - { - modulePaths.addAll( profile.getModules() ); - } - - for ( String modulePath : modulePaths ) - { - String module = relativizePath( new File( projectDir, modulePath ), projectsRoot ); - if ( module != null ) - { - collectProjects( projectsDir, module, projectPaths, false ); - } - } - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e ); - } - } - - /** - * Copies the specified projects to the directory given by {@link #cloneProjectsTo}. A project may either be denoted - * by a path to a POM file or merely by a path to a base directory. During cloning, the POM files will be filtered. - * - * @param projectPaths The paths to the projects to clone, relative to the projects directory, must not be - * <code>null</code> nor contain <code>null</code> elements. - * @throws org.apache.maven.plugin.MojoExecutionException If the the projects could not be copied/filtered. - */ - private void cloneProjects( Collection<String> projectPaths ) - throws MojoExecutionException - { - if ( !cloneProjectsTo.mkdirs() && cloneClean ) - { - try - { - FileUtils.cleanDirectory( cloneProjectsTo ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Could not clean the cloneProjectsTo directory. Reason: " - + e.getMessage(), e ); - } - } - - // determine project directories to clone - Collection<String> dirs = new LinkedHashSet<String>(); - for ( String projectPath : projectPaths ) - { - if ( !new File( projectsDirectory, projectPath ).isDirectory() ) - { - projectPath = getParentPath( projectPath ); - } - dirs.add( projectPath ); - } - - boolean filter; - - // clone project directories - try - { - filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() ); - - List<String> clonedSubpaths = new ArrayList<String>(); - - for ( String subpath : dirs ) - { - // skip this project if its parent directory is also scheduled for cloning - if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) ) - { - continue; - } - - // avoid copying subdirs that are already cloned. - if ( !alreadyCloned( subpath, clonedSubpaths ) ) - { - // avoid creating new files that point to dir/. - if ( ".".equals( subpath ) ) - { - String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() ); - - // avoid infinite recursion if the cloneTo path is a subdirectory. - if ( cloneSubdir != null ) - { - File temp = File.createTempFile( "pre-invocation-clone.", "" ); - temp.delete(); - temp.mkdirs(); - - copyDirectoryStructure( projectsDirectory, temp ); - - FileUtils.deleteDirectory( new File( temp, cloneSubdir ) ); - - copyDirectoryStructure( temp, cloneProjectsTo ); - } - else - { - copyDirectoryStructure( projectsDirectory, cloneProjectsTo ); - } - } - else - { - File srcDir = new File( projectsDirectory, subpath ); - File dstDir = new File( cloneProjectsTo, subpath ); - copyDirectoryStructure( srcDir, dstDir ); - } - - clonedSubpaths.add( subpath ); - } - } - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to clone projects from: " + projectsDirectory + " to: " - + cloneProjectsTo + ". Reason: " + e.getMessage(), e ); - } - - // filter cloned POMs - if ( filter ) - { - for ( String projectPath : projectPaths ) - { - File pomFile = new File( cloneProjectsTo, projectPath ); - if ( pomFile.isFile() ) - { - buildInterpolatedFile( pomFile, pomFile ); - } - - // MINVOKER-186 - // The following is a temporary solution to support Maven 3.3.1 (.mvn/extensions.xml) filtering - // Will be replaced by MINVOKER-117 with general filtering mechanism - File baseDir = pomFile.getParentFile(); - File mvnDir = new File( baseDir, ".mvn" ); - if ( mvnDir.isDirectory() ) - { - File extensionsFile = new File( mvnDir, "extensions.xml" ); - if ( extensionsFile.isFile() ) - { - buildInterpolatedFile( extensionsFile, extensionsFile ); - } - } - // END MINVOKER-186 - } - filteredPomPrefix = null; - } - } - - /** - * Gets the parent path of the specified relative path. - * - * @param path The relative path whose parent should be retrieved, must not be <code>null</code>. - * @return The parent path or "." if the specified path has no parent, never <code>null</code>. - */ - private String getParentPath( String path ) - { - int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) ); - return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep ); - } - - /** - * Copied a directory structure with default exclusions (.svn, CVS, etc) - * - * @param sourceDir The source directory to copy, must not be <code>null</code>. - * @param destDir The target directory to copy to, must not be <code>null</code>. - * @throws java.io.IOException If the directory structure could not be copied. - */ - private void copyDirectoryStructure( File sourceDir, File destDir ) - throws IOException - { - DirectoryScanner scanner = new DirectoryScanner(); - scanner.setBasedir( sourceDir ); - if ( !cloneAllFiles ) - { - scanner.addDefaultExcludes(); - } - scanner.scan(); - - /* - * NOTE: Make sure the destination directory is always there (even if empty) to support POM-less ITs. - */ - destDir.mkdirs(); - // Create all the directories, including any symlinks present in source - FileUtils.mkDirs( sourceDir, scanner.getIncludedDirectories(), destDir ); - - for ( String includedFile : scanner.getIncludedFiles() ) - { - File sourceFile = new File( sourceDir, includedFile ); - File destFile = new File( destDir, includedFile ); - FileUtils.copyFile( sourceFile, destFile ); - - // ensure clone project must be writable for additional changes - destFile.setWritable( true ); - } - } - - /** - * Determines whether the specified sub path has already been cloned, i.e. whether one of its ancestor directories - * was already cloned. - * - * @param subpath The sub path to check, must not be <code>null</code>. - * @param clonedSubpaths The list of already cloned paths, must not be <code>null</code> nor contain - * <code>null</code> elements. - * @return <code>true</code> if the specified path has already been cloned, <code>false</code> otherwise. - */ - static boolean alreadyCloned( String subpath, List<String> clonedSubpaths ) - { - for ( String path : clonedSubpaths ) - { - if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) ) - { - return true; - } - } - - return false; - } - - /** - * Runs the specified build jobs. - * - * @param projectsDir The base directory of all projects, must not be <code>null</code>. - * @param buildJobs The build jobs to run must not be <code>null</code> nor contain <code>null</code> elements. - * @throws org.apache.maven.plugin.MojoExecutionException If any build could not be launched. - */ - private void runBuilds( final File projectsDir, BuildJob[] buildJobs, int runWithParallelThreads ) - throws MojoExecutionException - { - if ( !localRepositoryPath.exists() ) - { - localRepositoryPath.mkdirs(); - } - - // ----------------------------------------------- - // interpolate settings file - // ----------------------------------------------- - - File interpolatedSettingsFile = interpolateSettings(); - - final File mergedSettingsFile = mergeSettings( interpolatedSettingsFile ); - - if ( mavenHome != null ) - { - actualMavenVersion = SelectorUtils.getMavenVersion( mavenHome ); - } - else - { - actualMavenVersion = SelectorUtils.getMavenVersion(); - } - scriptRunner.setGlobalVariable( "mavenVersion", actualMavenVersion ); - - final CharSequence actualJreVersion; - // @todo if ( javaVersions ) ... to be picked up from toolchains - if ( javaHome != null ) - { - actualJreVersion = resolveExternalJreVersion(); - } - else - { - actualJreVersion = SelectorUtils.getJreVersion(); - } - - try - { - if ( runWithParallelThreads > 1 ) - { - getLog().info( "use parallelThreads " + runWithParallelThreads ); - - ExecutorService executorService = Executors.newFixedThreadPool( runWithParallelThreads ); - for ( final BuildJob job : buildJobs ) - { - executorService.execute( new Runnable() - { - public void run() - { - try - { - runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion ); - } - catch ( MojoExecutionException e ) - { - throw new RuntimeException( e.getMessage(), e ); - } - } - } ); - } - - try - { - executorService.shutdown(); - // TODO add a configurable time out - executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS ); - } - catch ( InterruptedException e ) - { - throw new MojoExecutionException( e.getMessage(), e ); - } - } - else - { - for ( BuildJob job : buildJobs ) - { - runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion ); - } - } - } - finally - { - if ( interpolatedSettingsFile != null && cloneProjectsTo == null ) - { - interpolatedSettingsFile.delete(); - } - if ( mergedSettingsFile != null && mergedSettingsFile.exists() ) - { - mergedSettingsFile.delete(); - } - } - } - - /** - * Interpolate settings.xml file. - * - * @return The interpolated settings.xml file. - * @throws MojoExecutionException in case of a problem. - */ - private File interpolateSettings() - throws MojoExecutionException - { - File interpolatedSettingsFile = null; - if ( settingsFile != null ) - { - if ( cloneProjectsTo != null ) - { - interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() ); - } - else - { - interpolatedSettingsFile = - new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() ); - } - buildInterpolatedFile( settingsFile, interpolatedSettingsFile ); - } - return interpolatedSettingsFile; - } - - /** - * Merge the settings file - * - * @param interpolatedSettingsFile The interpolated settings file. - * @return The merged settings file. - * @throws MojoExecutionException Fail the build in case the merged settings file can't be created. - */ - private File mergeSettings( File interpolatedSettingsFile ) - throws MojoExecutionException - { - File mergedSettingsFile; - Settings mergedSettings = this.settings; - if ( mergeUserSettings ) - { - if ( interpolatedSettingsFile != null ) - { - // Have to merge the specified settings file (dominant) and the one of the invoking Maven process - try - { - SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); - request.setGlobalSettingsFile( interpolatedSettingsFile ); - - Settings dominantSettings = settingsBuilder.build( request ).getEffectiveSettings(); - Settings recessiveSettings = cloneSettings(); - SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL ); - - mergedSettings = dominantSettings; - getLog().debug( "Merged specified settings file with settings of invoking process" ); - } - catch ( SettingsBuildingException e ) - { - throw new MojoExecutionException( "Could not read specified settings file", e ); - } - } - } - - if ( this.settingsFile != null && !mergeUserSettings ) - { - mergedSettingsFile = interpolatedSettingsFile; - } - else - { - try - { - mergedSettingsFile = writeMergedSettingsFile( mergedSettings ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e ); - } - } - return mergedSettingsFile; - } - - private File writeMergedSettingsFile( Settings mergedSettings ) - throws IOException - { - File mergedSettingsFile; - mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" ); - - SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer(); - - FileWriter fileWriter = null; - try - { - fileWriter = new FileWriter( mergedSettingsFile ); - settingsWriter.write( fileWriter, mergedSettings ); - fileWriter.close(); - fileWriter = null; - } - finally - { - IOUtil.close( fileWriter ); - } - - if ( getLog().isDebugEnabled() ) - { - getLog().debug( "Created temporary file for invoker settings.xml: " - + mergedSettingsFile.getAbsolutePath() ); - } - return mergedSettingsFile; - } - - private Settings cloneSettings() - { - Settings recessiveSettings = SettingsUtils.copySettings( this.settings ); - - // MINVOKER-133: reset sourceLevelSet - resetSourceLevelSet( recessiveSettings ); - for ( org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors() ) - { - resetSourceLevelSet( mirror ); - } - for ( org.apache.maven.settings.Server server : recessiveSettings.getServers() ) - { - resetSourceLevelSet( server ); - } - for ( org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies() ) - { - resetSourceLevelSet( proxy ); - } - for ( org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles() ) - { - resetSourceLevelSet( profile ); - } - - return recessiveSettings; - } - - private void resetSourceLevelSet( org.apache.maven.settings.TrackableBase trackable ) - { - try - { - ReflectionUtils.setVariableValueInObject( trackable, "sourceLevelSet", Boolean.FALSE ); - getLog().debug( "sourceLevelSet: " - + ReflectionUtils.getValueIncludingSuperclasses( "sourceLevelSet", trackable ) ); - } - catch ( IllegalAccessException e ) - { - // noop - } - } - - private CharSequence resolveExternalJreVersion() - { - Artifact pluginArtifact = mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact(); - pluginArtifact.getFile(); - - Commandline commandLine = new Commandline(); - commandLine.setExecutable( new File( javaHome, "bin/java" ).getAbsolutePath() ); - commandLine.createArg().setValue( "-cp" ); - commandLine.createArg().setFile( pluginArtifact.getFile() ); - commandLine.createArg().setValue( SystemPropertyPrinter.class.getName() ); - commandLine.createArg().setValue( "java.version" ); - - final StringBuilder actualJreVersion = new StringBuilder(); - StreamConsumer consumer = new StreamConsumer() - { - public void consumeLine( String line ) - { - actualJreVersion.append( line ); - } - }; - try - { - CommandLineUtils.executeCommandLine( commandLine, consumer, null ); - } - catch ( CommandLineException e ) - { - getLog().warn( e.getMessage() ); - } - return actualJreVersion; - } - - /** - * Interpolate the pom file. - * - * @param pomFile The pom file. - * @param basedir The base directory. - * @return interpolated pom file location in case we have interpolated the pom file otherwise the original pom file - * will be returned. - * @throws MojoExecutionException - */ - private File interpolatePomFile( File pomFile, File basedir ) - throws MojoExecutionException - { - File interpolatedPomFile = null; - if ( pomFile != null ) - { - if ( StringUtils.isNotEmpty( filteredPomPrefix ) ) - { - interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() ); - buildInterpolatedFile( pomFile, interpolatedPomFile ); - } - else - { - interpolatedPomFile = pomFile; - } - } - return interpolatedPomFile; - } - - /** - * Runs the specified project. - * - * @param projectsDir The base directory of all projects, must not be <code>null</code>. - * @param buildJob The build job to run, must not be <code>null</code>. - * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code> to use - * the current user settings. - * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched. - */ - private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome, - CharSequence actualJreVersion ) - throws MojoExecutionException - { - // FIXME: Think about the following code part -- START - File pomFile = new File( projectsDir, buildJob.getProject() ); - File basedir; - if ( pomFile.isDirectory() ) - { - basedir = pomFile; - pomFile = new File( basedir, "pom.xml" ); - if ( !pomFile.exists() ) - { - pomFile = null; - } - else - { - buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" ); - } - } - else - { - basedir = pomFile.getParentFile(); - } - - File interpolatedPomFile = interpolatePomFile( pomFile, basedir ); - // FIXME: Think about the following code part -- ^^^^^^^ END - - getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() ); - - InvokerProperties invokerProperties = getInvokerProperties( basedir ); - - // let's set what details we can - buildJob.setName( invokerProperties.getJobName() ); - buildJob.setDescription( invokerProperties.getJobDescription() ); - - try - { - int selection = getSelection( invokerProperties, actualJreVersion ); - if ( selection == 0 ) - { - long milliseconds = System.currentTimeMillis(); - boolean executed; - try - { - // CHECKSTYLE_OFF: LineLength - executed = - runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties ); - // CHECKSTYLE_ON: LineLength - } - finally - { - milliseconds = System.currentTimeMillis() - milliseconds; - buildJob.setTime( milliseconds / 1000.0 ); - } - - if ( executed ) - { - buildJob.setResult( BuildJob.Result.SUCCESS ); - - if ( !suppressSummaries ) - { - getLog().info( pad( buildJob ).success( "SUCCESS" ).a( ' ' ) - + formatTime( buildJob.getTime() ) ); - } - } - else - { - buildJob.setResult( BuildJob.Result.SKIPPED ); - - if ( !suppressSummaries ) - { - getLog().info( pad( buildJob ).warning( "SKIPPED" ).a( ' ' ) - + formatTime( buildJob.getTime() ) ); - } - } - } - else - { - buildJob.setResult( BuildJob.Result.SKIPPED ); - - StringBuilder message = new StringBuilder(); - if ( selection == Selector.SELECTOR_MULTI ) - { - message.append( "non-matching selectors" ); - } - else - { - if ( ( selection & Selector.SELECTOR_MAVENVERSION ) != 0 ) - { - message.append( "Maven version" ); - } - if ( ( selection & Selector.SELECTOR_JREVERSION ) != 0 ) - { - if ( message.length() > 0 ) - { - message.append( ", " ); - } - message.append( "JRE version" ); - } - if ( ( selection & Selector.SELECTOR_OSFAMILY ) != 0 ) - { - if ( message.length() > 0 ) - { - message.append( ", " ); - } - message.append( "OS" ); - } - } - - if ( !suppressSummaries ) - { - getLog().info( pad( buildJob ).warning( "SKIPPED" ) + " due to " + message.toString() ); - } - - // Abuse failureMessage, the field in the report which should contain the reason for skipping - // Consider skipCode + I18N - buildJob.setFailureMessage( "Skipped due to " + message.toString() ); - } - } - catch ( RunErrorException e ) - { - buildJob.setResult( BuildJob.Result.ERROR ); - buildJob.setFailureMessage( e.getMessage() ); - - if ( !suppressSummaries ) - { - getLog().info( pad( buildJob ).failure( "ERROR" ).a( ' ' ) + formatTime( buildJob.getTime() ) ); - getLog().info( " " + e.getMessage() ); - } - } - catch ( RunFailureException e ) - { - buildJob.setResult( e.getType() ); - buildJob.setFailureMessage( e.getMessage() ); - - if ( !suppressSummaries ) - { - getLog().info( pad( buildJob ).failure( "FAILED" ).a( ' ' ) + formatTime( buildJob.getTime() ) ); - getLog().info( " " + e.getMessage() ); - } - } - finally - { - deleteInterpolatedPomFile( interpolatedPomFile ); - writeBuildReport( buildJob ); - } - } - - private MessageBuilder pad( BuildJob buildJob ) - { - MessageBuilder buffer = buffer( 128 ); - - buffer.a( " " ); - buffer.a( buildJob.getProject() ); - buffer.a( ' ' ); - - int l = 11 + buildJob.getProject().length(); - - if ( l < RESULT_COLUMN ) - { - for ( int i = RESULT_COLUMN - l; i > 0; i-- ) - { - buffer.a( '.' ); - } - } - - return buffer.a( ' ' ); - } - - /** - * Delete the interpolated pom file if it has been created before. - * - * @param interpolatedPomFile The interpolated pom file. - */ - private void deleteInterpolatedPomFile( File interpolatedPomFile ) - { - if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) ) - { - interpolatedPomFile.delete(); - } - } - - /** - * Determines whether selector conditions of the specified invoker properties match the current environment. - * - * @param invokerProperties The invoker properties to check, must not be <code>null</code>. - * @return <code>0</code> if the job corresponding to the properties should be run, otherwise a bitwise value - * representing the reason why it should be skipped. - */ - private int getSelection( InvokerProperties invokerProperties, CharSequence actualJreVersion ) - { - return new Selector( actualMavenVersion, actualJreVersion.toString() ).getSelection( invokerProperties ); - } - - /** - * Writes the XML report for the specified build job unless report generation has been disabled. - * - * @param buildJob The build job whose report should be written, must not be <code>null</code>. - * @throws org.apache.maven.plugin.MojoExecutionException If the report could not be written. - */ - private void writeBuildReport( BuildJob buildJob ) - throws MojoExecutionException - { - if ( disableReports ) - { - return; - } - - String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' ); - if ( safeFileName.endsWith( "_pom.xml" ) ) - { - safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() ); - } - - File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" ); - try - { - FileOutputStream fos = new FileOutputStream( reportFile ); - try - { - Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ); - BuildJobXpp3Writer writer = new BuildJobXpp3Writer(); - writer.write( osw, buildJob ); - osw.close(); - } - finally - { - fos.close(); - } - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to write build report " + reportFile, e ); - } - } - - /** - * Formats the specified build duration time. - * - * @param seconds The duration of the build. - * @return The formatted time, never <code>null</code>. - */ - private String formatTime( double seconds ) - { - return secFormat.format( seconds ); - } - - /** - * Runs the specified project. - * - * @param basedir The base directory of the project, must not be <code>null</code>. - * @param pomFile The (already interpolated) POM file, may be <code>null</code> for a POM-less Maven invocation. - * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code>. Will - * be merged with the settings file of the invoking Maven process. - * @param invokerProperties The properties to use. - * @return <code>true</code> if the project was launched or <code>false</code> if the selector script indicated that - * the project should be skipped. - * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched. - * @throws org.apache.maven.shared.scriptinterpreter.RunFailureException If either a hook script or the build itself - * failed. - */ - private boolean runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome, - InvokerProperties invokerProperties ) - throws MojoExecutionException, RunFailureException - { - if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() ) - { - Properties props = invokerProperties.getProperties(); - getLog().debug( "Using invoker properties:" ); - for ( String key : new TreeSet<String>( props.stringPropertyNames() ) ) - { - String value = props.getProperty( key ); - getLog().debug( " " + key + " = " + value ); - } - } - - List<String> goals = getGoals( basedir ); - - List<String> profiles = getProfiles( basedir ); - - Map<String, Object> context = new LinkedHashMap<String, Object>(); - - FileLogger logger = setupBuildLogFile( basedir ); - try - { - try - { - scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED, - false ); - } - catch ( RunErrorException e ) - { - throw e; - } - catch ( RunFailureException e ) - { - return false; - } - - scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger, - BuildJob.Result.FAILURE_PRE_HOOK, false ); - - final InvocationRequest request = new DefaultInvocationRequest(); - - request.setLocalRepositoryDirectory( localRepositoryPath ); - - request.setBatchMode( true ); - - request.setShowErrors( showErrors ); - - request.setDebug( debug ); - - request.setShowVersion( showVersion ); - - setupLoggerForBuildJob( logger, request ); - - if ( mavenHome != null ) - { - invoker.setMavenHome( mavenHome ); - // FIXME: Should we really take care of M2_HOME? - request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() ); - } - - if ( mavenExecutable != null ) - { - invoker.setMavenExecutable( new File( mavenExecutable ) ); - } - - if ( actualJavaHome != null ) - { - request.setJavaHome( actualJavaHome ); - } - - if ( environmentVariables != null ) - { - for ( Map.Entry<String, String> variable : environmentVariables.entrySet() ) - { - request.addShellEnvironment( variable.getKey(), variable.getValue() ); - } - } - - for ( int invocationIndex = 1;; invocationIndex++ ) - { - if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) ) - { - break; - } - - request.setBaseDirectory( basedir ); - - request.setPomFile( pomFile ); - - request.setGoals( goals ); - - request.setProfiles( profiles ); - - request.setMavenOpts( mavenOpts ); - - request.setOffline( false ); - - request.setUserSettingsFile( settingsFile ); - - Properties systemProperties = - getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) ); - request.setProperties( systemProperties ); - - invokerProperties.configureInvocation( request, invocationIndex ); - - if ( getLog().isDebugEnabled() ) - { - try - { - getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() ); - getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) ); - } - catch ( CommandLineConfigurationException e ) - { - getLog().debug( "Failed to display command line: " + e.getMessage() ); - } - } - - InvocationResult result; - - try - { - result = invoker.execute( request ); - } - catch ( final MavenInvocationException e ) - { - getLog().debug( "Error invoking Maven: " + e.getMessage(), e ); - throw new RunFailureException( "Maven invocation failed. " + e.getMessage(), - BuildJob.Result.FAILURE_BUILD ); - } - - verify( result, invocationIndex, invokerProperties, logger ); - } - - scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger, - BuildJob.Result.FAILURE_POST_HOOK, true ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( e.getMessage(), e ); - } - finally - { - if ( logger != null ) - { - logger.close(); - } - } - return true; - } - - private void setupLoggerForBuildJob( FileLogger logger, final InvocationRequest request ) - { - if ( logger != null ) - { - request.setErrorHandler( logger ); - - request.setOutputHandler( logger ); - } - } - - /** - * Initializes the build logger for the specified project. This will write the logging information into - * {@code build.log}. - * - * @param basedir The base directory of the project, must not be <code>null</code>. - * @return The build logger or <code>null</code> if logging has been disabled. - * @throws org.apache.maven.plugin.MojoExecutionException If the log file could not be created. - */ - private FileLogger setupBuildLogFile( File basedir ) - throws MojoExecutionException - { - FileLogger logger = null; - - if ( !noLog ) - { - File outputLog = new File( basedir, "build.log" ); - try - { - if ( streamLogs ) - { - logger = new FileLogger( outputLog, getLog() ); - } - else - { - logger = new FileLogger( outputLog ); - } - - getLog().debug( "Build log initialized in: " + outputLog ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e ); - } - } - - return logger; - } - - /** - * Gets the system properties to use for the specified project. - * - * @param basedir The base directory of the project, must not be <code>null</code>. - * @param filename The filename to the properties file to load, may be <code>null</code> to use the default path - * given by {@link #testPropertiesFile}. - * @return The system properties to use, may be empty but never <code>null</code>. - * @throws org.apache.maven.plugin.MojoExecutionException If the properties file exists but could not be read. - */ - private Properties getSystemProperties( final File basedir, final String filename ) - throws MojoExecutionException - { - Properties collectedTestProperties = new Properties(); - - if ( properties != null ) - { - // MINVOKER-118: property can have empty value, which is not accepted by collectedTestProperties - for ( Map.Entry<String, String> entry : properties.entrySet() ) - { - if ( entry.getValue() != null ) - { - collectedTestProperties.put( entry.getKey(), entry.getValue() ); - } - } - } - - File propertiesFile = null; - if ( filename != null ) - { - propertiesFile = new File( basedir, filename ); - } - else if ( testPropertiesFile != null ) - { - propertiesFile = new File( basedir, testPropertiesFile ); - } - - if ( propertiesFile != null && propertiesFile.isFile() ) - { - InputStream fin = null; - try - { - fin = new FileInputStream( propertiesFile ); - - Properties loadedProperties = new Properties(); - loadedProperties.load( fin ); - fin.close(); - fin = null; - collectedTestProperties.putAll( loadedProperties ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Error reading system properties from " + propertiesFile ); - } - finally - { - IOUtil.close( fin ); - } - } - - return collectedTestProperties; - } - - /** - * Verifies the invocation result. - * - * @param result The invocation result to check, must not be <code>null</code>. - * @param invocationIndex The index of the invocation for which to check the exit code, must not be negative. - * @param invokerProperties The invoker properties used to check the exit code, must not be <code>null</code>. - * @param logger The build logger, may be <code>null</code> if logging is disabled. - * @throws org.apache.maven.shared.scriptinterpreter.RunFailureException If the invocation result indicates a build - * failure. - */ - private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties, - FileLogger logger ) - throws RunFailureException - { - if ( result.getExecutionException() != null ) - { - throw new RunFailureException( "The Maven invocation failed. " - + result.getExecutionException().getMessage(), BuildJob.Result.ERROR ); - } - else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) ) - { - StringBuilder buffer = new StringBuilder( 256 ); - buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " ); - if ( logger != null ) - { - buffer.append( "See " ); - buffer.append( logger.getOutputFile().getAbsolutePath() ); - buffer.append( " for details." ); - } - else - { - buffer.append( "See console output for details." ); - } - throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD ); - } - } - - /** - * Gets the goal list for the specified project. - * - * @param basedir The base directory of the project, must not be <code>null</code>. - * @return The list of goals to run when building the project, may be empty but never <code>null</code>. - * @throws org.apache.maven.plugin.MojoExecutionException If the profile file could not be read. - */ - List<String> getGoals( final File basedir ) - throws MojoExecutionException - { - try - { - // FIXME: Currently we have null for goalsFile which has been removed. - // This might mean we can remove getGoals() at all ? Check this. - return getTokens( basedir, null, goals ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "error reading goals", e ); - } - } - - /** - * Gets the profile list for the specified project. - * - * @param basedir The base directory of the project, must not be <code>null</code>. - * @return The list of profiles to activate when building the project, may be empty but never <code>null</code>. - * @throws org.apache.maven.plugin.MojoExecutionException If the profile file could not be read. - */ - List<String> getProfiles( File basedir ) - throws MojoExecutionException - { - try - { - return getTokens( basedir, null, profiles ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "error reading profiles", e ); - } - } - - private List<String> calculateExcludes() - throws IOException - { - List<String> excludes = - ( pomExcludes != null ) ? new ArrayList<String>( pomExcludes ) : new ArrayList<String>(); - if ( this.settingsFile != null ) - { - String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() ); - if ( exclude != null ) - { - excludes.add( exclude.replace( '\\', '/' ) ); - getLog().debug( "Automatically excluded " + exclude + " from project scanning" ); - } - } - return excludes; - - } - - /** - * @return The list of setupUp jobs. - * @throws IOException - * @see {@link #setupIncludes} - */ - private BuildJob[] getSetupBuildJobsFromFolders() - throws IOException - { - List<String> excludes = calculateExcludes(); - - BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); - if ( getLog().isDebugEnabled() ) - { - getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); - } - - return setupPoms; - } - - /** - * Gets the build jobs that should be processed. Note that the order of the returned build jobs is significant. - * - * @return The build jobs to process, may be empty but never <code>null</code>. - * @throws java.io.IOException If the projects directory could not be scanned. - */ - BuildJob[] getBuildJobs() - throws IOException - { - BuildJob[] buildJobs; - - if ( invokerTest == null ) - { - List<String> excludes = calculateExcludes(); - - BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); - if ( getLog().isDebugEnabled() ) - { - getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); - } - - BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL ); - - Map<String, BuildJob> uniquePoms = new LinkedHashMap<String, BuildJob>(); - for ( BuildJob setupPom : setupPoms ) - { - uniquePoms.put( setupPom.getProject(), setupPom ); - } - for ( BuildJob normalPom : normalPoms ) - { - if ( !uniquePoms.containsKey( normalPom.getProject() ) ) - { - uniquePoms.put( normalPom.getProject(), normalPom ); - } - } - - buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] ); - } - else - { - String[] testRegexes = StringUtils.split( invokerTest, "," ); - List<String> includes = new ArrayList<String>( testRegexes.length ); - List<String> excludes = new ArrayList<String>(); - - for ( String regex : testRegexes ) - { - // user just use -Dinvoker.test=MWAR191,MNG111 to use a directory thats the end is not pom.xml - if ( regex.startsWith( "!" ) ) - { - excludes.add( regex.substring( 1 ) ); - } - else - { - includes.add( regex ); - } - } - - // it would be nice if we could figure out what types these are... but perhaps - // not necessary for the -Dinvoker.test=xxx t - buildJobs = scanProjectsDirectory( includes, excludes, BuildJob.Type.DIRECT ); - } - - relativizeProjectPaths( buildJobs ); - - return buildJobs; - } - - /** - * Scans the projects directory for projects to build. Both (POM) files and mere directories will be matched by the - * scanner patterns. If the patterns match a directory which contains a file named "pom.xml", the results will - * include the path to this file rather than the directory path in order to avoid duplicate invocations of the same - * project. - * - * @param includes The include patterns for the scanner, may be <code>null</code>. - * @param excludes The exclude patterns for the scanner, may be <code>null</code> to exclude nothing. - * @param type The type to assign to the resulting build jobs, must not be <code>null</code>. - * @return The build jobs matching the patterns, never <code>null</code>. - * @throws java.io.IOException If the project directory could not be scanned. - */ - private BuildJob[] scanProjectsDirectory( List<String> includes, List<String> excludes, String type ) - throws IOException - { - if ( !projectsDirectory.isDirectory() ) - { - return new BuildJob[0]; - } - - DirectoryScanner scanner = new DirectoryScanner(); - scanner.setBasedir( projectsDirectory.getCanonicalFile() ); - scanner.setFollowSymlinks( false ); - if ( includes != null ) - { - scanner.setIncludes( includes.toArray( new String[includes.size()] ) ); - } - if ( excludes != null ) - { - scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) ); - } - scanner.addDefaultExcludes(); - scanner.scan(); - - Map<String, BuildJob> matches = new LinkedHashMap<String, BuildJob>(); - - for ( String includedFile : scanner.getIncludedFiles() ) - { - matches.put( includedFile, new BuildJob( includedFile, type ) ); - } - - for ( String includedDir : scanner.getIncludedDirectories() ) - { - String includedFile = includedDir + File.separatorChar + "pom.xml"; - if ( new File( scanner.getBasedir(), includedFile ).isFile() ) - { - matches.put( includedFile, new BuildJob( includedFile, type ) ); - } - else - { - matches.put( includedDir, new BuildJob( includedDir, type ) ); - } - } - - return matches.values().toArray( new BuildJob[matches.size()] ); - } - - /** - * Relativizes the project paths of the specified build jobs against the directory specified by - * {@link #projectsDirectory} (if possible). If a project path does not denote a sub path of the projects directory, - * it is returned as is. - * - * @param buildJobs The build jobs whose project paths should be relativized, must not be <code>null</code> nor - * contain <code>null</code> elements. - * @throws java.io.IOException If any path could not be relativized. - */ - private void relativizeProjectPaths( BuildJob[] buildJobs ) - throws IOException - { - String projectsDirPath = projectsDirectory.getCanonicalPath(); - - for ( BuildJob buildJob : buildJobs ) - { - String projectPath = buildJob.getProject(); - - File file = new File( projectPath ); - - if ( !file.isAbsolute() ) - { - file = new File( projectsDirectory, projectPath ); - } - - String relativizedPath = relativizePath( file, projectsDirPath ); - - if ( relativizedPath == null ) - { - relativizedPath = projectPath; - } - - buildJob.setProject( relativizedPath ); - } - } - - /** - * Relativizes the specified path against the given base directory. Besides relativization, the returned path will - * also be normalized, e.g. directory references like ".." will be removed. - * - * @param path The path to relativize, must not be <code>null</code>. - * @param basedir The (canonical path of the) base directory to relativize against, must not be <code>null</code>. - * @return The relative path in normal form or <code>null</code> if the input path does not denote a sub path of the - * base directory. - * @throws java.io.IOException If the path could not be relativized. - */ - private String relativizePath( File path, String basedir ) - throws IOException - { - String relativizedPath = path.getCanonicalPath(); - - if ( relativizedPath.startsWith( basedir ) ) - { - relativizedPath = relativizedPath.substring( basedir.length() ); - if ( relativizedPath.startsWith( File.separator ) ) - { - relativizedPath = relativizedPath.substring( File.separator.length() ); - } - - return relativizedPath; - } - else - { - return null; - } - } - - /** - * Returns the map-based value source used to interpolate POMs and other stuff. - * - * @param escapeXml {@code true}, to escape any XML special characters in the property values; {@code false}, to not - * escape any property values. - * - * @return The map-based value source for interpolation, never <code>null</code>. - */ - private Map<String, Object> getInterpolationValueSource( final boolean escapeXml ) - { - Map<String, Object> props = new HashMap<String, Object>(); - - if ( filterProperties != null ) - { - props.putAll( filterProperties ); - } - props.put( "basedir", this.project.getBasedir().getAbsolutePath() ); - props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) ); - if ( settings.getLocalRepository() != null ) - { - props.put( "localRepository", settings.getLocalRepository() ); - props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) ); - } - - return new CompositeMap( this.project, props, escapeXml ); - } - - /** - * Converts the specified filesystem path to a URL. The resulting URL has no trailing slash regardless whether the - * path denotes a file or a directory. - * - * @param filename The filesystem path to convert, must not be <code>null</code>. - * @return The <code>file:</code> URL for the specified path, never <code>null</code>. - */ - private static String toUrl( String filename ) - { - /* - * NOTE: Maven fails to properly handle percent-encoded "file:" URLs (WAGON-111) so don't use File.toURI() here - * as-is but use the decoded path component in the URL. - */ - String url = "file://" + new File( filename ).toURI().getPath(); - if ( url.endsWith( "/" ) ) - { - url = url.substring( 0, url.length() - 1 ); - } - return url; - } - - /** - * Gets goal/profile names for the specified project, either directly from the plugin configuration or from an - * external token file. - * - * @param basedir The base directory of the test project, must not be <code>null</code>. - * @param filename The (simple) name of an optional file in the project base directory from which to read - * goals/profiles, may be <code>null</code>. - * @param defaultTokens The list of tokens to return in case the specified token file does not exist, may be - * <code>null</code>. - * @return The list of goal/profile names, may be empty but never <code>null</code>. - * @throws java.io.IOException If the token file exists but could not be parsed. - */ - private List<String> getTokens( File basedir, String filename, List<String> defaultTokens ) - throws IOException - { - List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>(); - - if ( StringUtils.isNotEmpty( filename ) ) - { - File tokenFile = new File( basedir, filename ); - - if ( tokenFile.exists() ) - { - tokens = readTokens( tokenFile ); - } - } - - return tokens; - } - - /** - * Reads the tokens from the specified file. Tokens are separated either by line terminators or commas. During - * parsing, the file contents will be interpolated. - * - * @param tokenFile The file to read the tokens from, must not be <code>null</code>. - * @return The list of tokens, may be empty but never <code>null</code>. - * @throws java.io.IOException If the token file could not be read. - */ - private List<String> readTokens( final File tokenFile ) - throws IOException - { - List<String> result = new ArrayList<String>(); - - BufferedReader reader = null; - try - { - Map<String, Object> composite = getInterpolationValueSource( false ); - reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) ); - - for ( String line = reader.readLine(); line != null; line = reader.readLine() ) - { - result.addAll( collectListFromCSV( line ) ); - } - - reader.close(); - reader = null; - } - finally - { - IOUtil.close( reader ); - } - - return result; - } - - /** - * Gets a list of comma separated tokens from the specified line. - * - * @param csv The line with comma separated tokens, may be <code>null</code>. - * @return The list of tokens from the line, may be empty but never <code>null</code>. - */ - private List<String> collectListFromCSV( final String csv ) - { - final List<String> result = new ArrayList<String>(); - - if ( ( csv != null ) && ( csv.trim().length() > 0 ) ) - { - final StringTokenizer st = new StringTokenizer( csv, "," ); - - while ( st.hasMoreTokens() ) - { - result.add( st.nextToken().trim() ); - } - } - - return result; - } - - /** - * Interpolates the specified POM/settings file to a temporary file. The destination file may be same as the input - * file, i.e. interpolation can be performed in-place. - * <p> - * <b>Note:</b>This methods expects the file to be a XML file and applies special XML escaping during interpolation. - * </p> - * - * @param originalFile The XML file to interpolate, must not be <code>null</code>. - * @param interpolatedFile The target file to write the interpolated contents of the original file to, must not be - * <code>null</code>. - * - * @throws org.apache.maven.plugin.MojoExecutionException If the target file could not be created. - */ - void buildInterpolatedFile( File originalFile, File interpolatedFile ) - throws MojoExecutionException - { - getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() ); - - try - { - String xml; - - Reader reader = null; - try - { - // interpolation with token @...@ - Map<String, Object> composite = getInterpolationValueSource( true ); - reader = - new InterpolationFilterReader( ReaderFactory.newXmlReader( originalFile ), composite, "@", "@" ); - - xml = IOUtil.toString( reader ); - - reader.close(); - reader = null; - } - finally - { - IOUtil.close( reader ); - } - - Writer writer = null; - try - { - interpolatedFile.getParentFile().mkdirs(); - writer = WriterFactory.newXmlWriter( interpolatedFile ); - writer.write( xml ); - writer.close(); - writer = null; - } - finally - { - IOUtil.close( writer ); - } - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e ); - } - } - - /** - * Gets the (interpolated) invoker properties for an integration test. - * - * @param projectDirectory The base directory of the IT project, must not be <code>null</code>. - * @return The invoker properties, may be empty but never <code>null</code>. - * @throws org.apache.maven.plugin.MojoExecutionException If an I/O error occurred during reading the properties. - */ - private InvokerProperties getInvokerProperties( final File projectDirectory ) - throws MojoExecutionException - { - Properties props = new Properties(); - if ( invokerPropertiesFile != null ) - { - File propertiesFile = new File( projectDirectory, invokerPropertiesFile ); - if ( propertiesFile.isFile() ) - { - InputStream in = null; - try - { - in = new FileInputStream( propertiesFile ); - props.load( in ); - in.close(); - in = null; - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e ); - } - finally - { - IOUtil.close( in ); - } - } - - Interpolator interpolator = new RegexBasedInterpolator(); - interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) ); - // CHECKSTYLE_OFF: LineLength - for ( String key : props.stringPropertyNames() ) - { - String value = props.getProperty( key ); - try - { - value = interpolator.interpolate( value, "" ); - } - catch ( InterpolationException e ) - { - throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile, - e ); - } - props.setProperty( key, value ); - } - // CHECKSTYLE_ON: LineLength - } - return new InvokerProperties( props ); - } - - protected boolean isParallelRun() - { - return parallelThreads > 1; - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.TreeSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.model.Model; +import org.apache.maven.model.Profile; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.invoker.model.BuildJob; +import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Writer; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.apache.maven.settings.SettingsUtils; +import org.apache.maven.settings.TrackableBase; +import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; +import org.apache.maven.settings.building.SettingsBuilder; +import org.apache.maven.settings.building.SettingsBuildingException; +import org.apache.maven.settings.building.SettingsBuildingRequest; +import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer; +import org.apache.maven.shared.invoker.CommandLineConfigurationException; +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.invoker.Invoker; +import org.apache.maven.shared.invoker.MavenCommandLineBuilder; +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.apache.maven.shared.scriptinterpreter.RunErrorException; +import org.apache.maven.shared.scriptinterpreter.RunFailureException; +import org.apache.maven.shared.scriptinterpreter.ScriptRunner; +import org.apache.maven.shared.utils.logging.MessageBuilder; +import org.codehaus.plexus.interpolation.InterpolationException; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.RegexBasedInterpolator; +import org.codehaus.plexus.util.DirectoryScanner; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.ReflectionUtils; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.cli.CommandLineException; +import org.codehaus.plexus.util.cli.CommandLineUtils; +import org.codehaus.plexus.util.cli.Commandline; +import org.codehaus.plexus.util.cli.StreamConsumer; +import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; + +/** + * Provides common code for mojos invoking sub builds. + * + * @author Stephen Connolly + * @since 15-Aug-2009 09:09:29 + */ +public abstract class AbstractInvokerMojo + extends AbstractMojo +{ + /** + * The zero-based column index where to print the invoker result. + */ + private static final int RESULT_COLUMN = 60; + + /** + * Flag used to suppress certain invocations. This is useful in tailoring the build using profiles. + * + * @since 1.1 + */ + @Parameter( property = "invoker.skip", defaultValue = "false" ) + private boolean skipInvocation; + + /** + * Flag used to suppress the summary output notifying of successes and failures. If set to <code>true</code>, the + * only indication of the build's success or failure will be the effect it has on the main build (if it fails, the + * main build should fail as well). If {@link #streamLogs} is enabled, the sub-build summary will also provide an + * indication. + */ + @Parameter( defaultValue = "false" ) + protected boolean suppressSummaries; + + /** + * Flag used to determine whether the build logs should be output to the normal mojo log. + */ + @Parameter( property = "invoker.streamLogs", defaultValue = "false" ) + private boolean streamLogs; + + /** + * The local repository for caching artifacts. It is strongly recommended to specify a path to an isolated + * repository like <code>${project.build.directory}/it-repo</code>. Otherwise, your ordinary local repository will + * be used, potentially soiling it with broken artifacts. + */ + @Parameter( property = "invoker.localRepositoryPath", defaultValue = "${settings.localRepository}" ) + private File localRepositoryPath; + + /** + * Directory to search for integration tests. + */ + @Parameter( property = "invoker.projectsDirectory", defaultValue = "${basedir}/src/it/" ) + private File projectsDirectory; + + /** + * Base directory where all build reports are written to. Every execution of an integration test will produce an XML + * file which contains the information about success or failure of that particular build job. The format of the + * resulting XML file is documented in the given <a href="./build-job.html">build-job</a> reference. + * + * @since 1.4 + */ + @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" ) + private File reportsDirectory; + + /** + * A flag to disable the generation of build reports. + * + * @since 1.4 + */ + @Parameter( property = "invoker.disableReports", defaultValue = "false" ) + private boolean disableReports; + + /** + * Directory to which projects should be cloned prior to execution. If not specified, each integration test will be + * run in the directory in which the corresponding IT POM was found. In this case, you most likely want to configure + * your SCM to ignore <code>target</code> and <code>build.log</code> in the test's base directory. + * + * @since 1.1 + */ + @Parameter + private File cloneProjectsTo; + + // CHECKSTYLE_OFF: LineLength + /** + * Some files are normally excluded when copying the IT projects from the directory specified by the parameter + * projectsDirectory to the directory given by cloneProjectsTo (e.g. <code>.svn</code>, <code>CVS</code>, + * <code>*~</code>, etc: see <a href= + * "https://codehaus-plexus.github.io/plexus-utils/apidocs/org/codehaus/plexus/util/AbstractScanner.html#DEFAULTEXCLUDES"> + * reference</a> for full list). Setting this parameter to <code>true</code> will cause all files to be copied to + * the <code>cloneProjectsTo</code> directory. + * + * @since 1.2 + */ + @Parameter( defaultValue = "false" ) + private boolean cloneAllFiles; + // CHECKSTYLE_ON: LineLength + + /** + * Ensure the {@link #cloneProjectsTo} directory is not polluted with files from earlier invoker runs. + * + * @since 1.6 + */ + @Parameter( defaultValue = "false" ) + private boolean cloneClean; + + /** + * A single POM to build, skipping any scanning parameters and behavior. + */ + @Parameter( property = "invoker.pom" ) + private File pom; + + /** + * Include patterns for searching the integration test directory for projects. This parameter is meant to be set + * from the POM. If this parameter is not set, the plugin will search for all <code>pom.xml</code> files one + * directory below {@link #projectsDirectory} (i.e. <code>*/pom.xml</code>).<br> + * <br> + * Starting with version 1.3, mere directories can also be matched by these patterns. For example, the include + * pattern <code>*</code> will run Maven builds on all immediate sub directories of {@link #projectsDirectory}, + * regardless if they contain a <code>pom.xml</code>. This allows to perform builds that need/should not depend on + * the existence of a POM. + */ + @Parameter + private List<String> pomIncludes = Collections.singletonList( "*/pom.xml" ); + + /** + * Exclude patterns for searching the integration test directory. This parameter is meant to be set from the POM. By + * default, no POM files are excluded. For the convenience of using an include pattern like <code>*</code>, the + * custom settings file specified by the parameter {@link #settingsFile} will always be excluded automatically. + */ + @Parameter + private List<String> pomExcludes = Collections.emptyList(); + + /** + * Include patterns for searching the projects directory for projects that need to be run before the other projects. + * This parameter allows to declare projects that perform setup tasks like installing utility artifacts into the + * local repository. Projects matched by these patterns are implicitly excluded from the scan for ordinary projects. + * Also, the exclusions defined by the parameter {@link #pomExcludes} apply to the setup projects, too. Default + * value is: <code>setup*/pom.xml</code>. + * + * @since 1.3 + */ + @Parameter + private List<String> setupIncludes = Collections.singletonList( "setup*/pom.xml" ); + + /** + * The list of goals to execute on each project. Default value is: <code>package</code>. + */ + @Parameter + private List<String> goals = Collections.singletonList( "package" ); + + /** + */ + @Component + private Invoker invoker; + + @Component + private SettingsBuilder settingsBuilder; + + /** + * Relative path of a selector script to run prior in order to decide if the build should be executed. This script + * may be written with either BeanShell or Groovy. If the file extension is omitted (e.g. <code>selector</code>), + * the plugin searches for the file by trying out the well-known extensions <code>.bsh</code> and + * <code>.groovy</code>. If this script exists for a particular project but returns any non-null value different + * from <code>true</code>, the corresponding build is flagged as skipped. In this case, none of the pre-build hook + * script, Maven nor the post-build hook script will be invoked. If this script throws an exception, the + * corresponding build is flagged as in error, and none of the pre-build hook script, Maven not the post-build hook + * script will be invoked. + * + * @since 1.5 + */ + @Parameter( property = "invoker.selectorScript", defaultValue = "selector" ) + private String selectorScript; + + /** + * Relative path of a pre-build hook script to run prior to executing the build. This script may be written with + * either BeanShell or Groovy (since 1.3). If the file extension is omitted (e.g. <code>prebuild</code>), the plugin + * searches for the file by trying out the well-known extensions <code>.bsh</code> and <code>.groovy</code>. If this + * script exists for a particular project but returns any non-null value different from <code>true</code> or throws + * an exception, the corresponding build is flagged as a failure. In this case, neither Maven nor the post-build + * hook script will be invoked. + */ + @Parameter( property = "invoker.preBuildHookScript", defaultValue = "prebuild" ) + private String preBuildHookScript; + + /** + * Relative path of a cleanup/verification hook script to run after executing the build. This script may be written + * with either BeanShell or Groovy (since 1.3). If the file extension is omitted (e.g. <code>verify</code>), the + * plugin searches for the file by trying out the well-known extensions <code>.bsh</code> and <code>.groovy</code>. + * If this script exists for a particular project but returns any non-null value different from <code>true</code> or + * throws an exception, the corresponding build is flagged as a failure. + */ + @Parameter( property = "invoker.postBuildHookScript", defaultValue = "postbuild" ) + private String postBuildHookScript; + + /** + * Location of a properties file that defines CLI properties for the test. + */ + @Parameter( property = "invoker.testPropertiesFile", defaultValue = "test.properties" ) + private String testPropertiesFile; + + /** + * Common set of properties to pass in on each project's command line, via -D parameters. + * + * @since 1.1 + */ + @Parameter + private Map<String, String> properties; + + /** + * Whether to show errors in the build output. + */ + @Parameter( property = "invoker.showErrors", defaultValue = "false" ) + private boolean showErrors; + + /** + * Whether to show debug statements in the build output. + */ + @Parameter( property = "invoker.debug", defaultValue = "false" ) + private boolean debug; + + /** + * Suppress logging to the <code>build.log</code> file. + */ + @Parameter( property = "invoker.noLog", defaultValue = "false" ) + private boolean noLog; + + /** + * List of profile identifiers to explicitly trigger in the build. + * + * @since 1.1 + */ + @Parameter + private List<String> profiles; + + /** + * A list of additional properties which will be used to filter tokens in POMs and goal files. + * + * @since 1.3 + */ + @Parameter + private Map<String, String> filterProperties; + + /** + * The Maven Project Object + * + * @since 1.1 + */ + @Parameter( defaultValue = "${project}", readonly = true, required = true ) + private MavenProject project; + + @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true ) + private MojoExecution mojoExecution; + + /** + * A comma separated list of projectname patterns to run. Specify this parameter to run individual tests by file + * name, overriding the {@link #setupIncludes}, {@link #pomIncludes} and {@link #pomExcludes} parameters. Each + * pattern you specify here will be used to create an include/exclude pattern formatted like + * <code>${projectsDirectory}/<i>pattern</i></code>. To exclude a test, prefix the pattern with a '<code>!</code>'. + * So you can just type <nobr><code>-Dinvoker.test=SimpleTest,Comp*Test,!Compare*</code></nobr> to run builds in + * <code>${projectsDirectory}/SimpleTest</code> and <code>${projectsDirectory}/ComplexTest</code>, but not + * <code>${projectsDirectory}/CompareTest</code> + * + * @since 1.1 (exclusion since 1.8) + */ + @Parameter( property = "invoker.test" ) + private String invokerTest; + + /** + * Path to an alternate <code>settings.xml</code> to use for Maven invocation with all ITs. Note that the + * <code><localRepository></code> element of this settings file is always ignored, i.e. the path given by the + * parameter {@link #localRepositoryPath} is dominant. + * + * @since 1.2 + */ + @Parameter( property = "invoker.settingsFile" ) + private File settingsFile; + + /** + * The <code>MAVEN_OPTS</code> environment variable to use when invoking Maven. This value can be overridden for + * individual integration tests by using {@link #invokerPropertiesFile}. + * + * @since 1.2 + */ + @Parameter( property = "invoker.mavenOpts" ) + private String mavenOpts; + + /** + * The home directory of the Maven installation to use for the forked builds. Defaults to the current Maven + * installation. + * + * @since 1.3 + */ + @Parameter( property = "invoker.mavenHome" ) + private File mavenHome; + + /** + * mavenExecutable can either be a file relative to <code>${maven.home}/bin/</code> or an absolute file. + * + * @since 1.8 + * @see Invoker#setMavenExecutable(File) + */ + @Parameter( property = "invoker.mavenExecutable" ) + private String mavenExecutable; + + /** + * The <code>JAVA_HOME</code> environment variable to use for forked Maven invocations. Defaults to the current Java + * home directory. + * + * @since 1.3 + */ + @Parameter( property = "invoker.javaHome" ) + private File javaHome; + + /** + * The file encoding for the pre-/post-build scripts and the list files for goals and profiles. + * + * @since 1.2 + */ + @Parameter( property = "encoding", defaultValue = "${project.build.sourceEncoding}" ) + private String encoding; + + /** + * The current user system settings for use in Maven. + * + * @since 1.2 + */ + @Parameter( defaultValue = "${settings}", readonly = true, required = true ) + private Settings settings; + + /** + * A flag whether the test class path of the project under test should be included in the class path of the + * pre-/post-build scripts. If set to <code>false</code>, the class path of script interpreter consists only of the + * <a href="dependencies.html">runtime dependencies</a> of the Maven Invoker Plugin. If set the <code>true</code>, + * the project's test class path will be prepended to the interpreter class path. Among others, this feature allows + * the scripts to access utility classes from the test sources of your project. + * + * @since 1.2 + */ + @Parameter( property = "invoker.addTestClassPath", defaultValue = "false" ) + private boolean addTestClassPath; + + /** + * The test class path of the project under test. + */ + @Parameter( defaultValue = "${project.testClasspathElements}", readonly = true ) + private List<String> testClassPath; + + /** + * The name of an optional project-specific file that contains properties used to specify settings for an individual + * Maven invocation. Any property present in the file will override the corresponding setting from the plugin + * configuration. The values of the properties are filtered and may use expressions like + * <code>${project.version}</code> to reference project properties or values from the parameter + * {@link #filterProperties}. The snippet below describes the supported properties: + * <p/> + * + * <pre> + * # A comma or space separated list of goals/phases to execute, may + * # specify an empty list to execute the default goal of the IT project. + * # Environment variables used by maven plugins can be added here + * invoker.goals = clean install -Dplugin.variable=value + * + * # Or you can give things like this if you need. + * invoker.goals = -T2 clean verify + * + * # Optionally, a list of goals to run during further invocations of Maven + * invoker.goals.2 = ${project.groupId}:${project.artifactId}:${project.version}:run + * + * # A comma or space separated list of profiles to activate + * invoker.profiles = its,jdk15 + * + * # The path to an alternative POM or base directory to invoke Maven on, defaults to the + * # project that was originally specified in the plugin configuration + * # Since plugin version 1.4 + * invoker.project = sub-module + * + * # The value for the environment variable MAVEN_OPTS + * invoker.mavenOpts = -Dfile.encoding=UTF-16 -Xms32m -Xmx256m + * + * # Possible values are "fail-fast" (default), "fail-at-end" and "fail-never" + * invoker.failureBehavior = fail-never + * + * # The expected result of the build, possible values are "success" (default) and "failure" + * invoker.buildResult = failure + * + * # A boolean value controlling the aggregator mode of Maven, defaults to "false" + * invoker.nonRecursive = true + * + * # A boolean value controlling the network behavior of Maven, defaults to "false" + * # Since plugin version 1.4 + * invoker.offline = true + * + * # The path to the properties file from which to load system properties, defaults to the + * # filename given by the plugin parameter testPropertiesFile + * # Since plugin version 1.4 + * invoker.systemPropertiesFile = test.properties + * + * # An optional human friendly name for this build job to be included in the build reports. + * # Since plugin version 1.4 + * invoker.name = Test Build 01 + * + * # An optional description for this build job to be included in the build reports. + * # Since plugin version 1.4 + * invoker.description = Checks the support for build reports. + * + * # A comma separated list of JRE versions on which this build job should be run. + * # Since plugin version 1.4 + * invoker.java.version = 1.4+, !1.4.1, 1.7- + * + * # A comma separated list of OS families on which this build job should be run. + * # Since plugin version 1.4 + * invoker.os.family = !windows, unix, mac + * + * # A comma separated list of Maven versions on which this build should be run. + * # Since plugin version 1.5 + * invoker.maven.version = 2.0.10+, !2.1.0, !2.2.0 + * + * # For java.version, maven.version and os.family it is possible to define multiple selectors. + * # If one of the indexed selectors matches, the test is executed. + * # With the invoker.x.y equivalents you can specify global matchers. + * selector.1.java.version = 1.8+ + * selector.1.maven.version = 3.2.5+ + * selector.1.os.family = !windows + * selector.2.maven.version = 3.0+ + * selector.3.java.version = 9+ + * + * # A boolean value controlling the debug logging level of Maven, , defaults to "false" + * # Since plugin version 1.8 + * invoker.debug = true + * </pre> + * + * @since 1.2 + */ + @Parameter( property = "invoker.invokerPropertiesFile", defaultValue = "invoker.properties" ) + private String invokerPropertiesFile; + + /** + * flag to enable show mvn version used for running its (cli option : -V,--show-version ) + * + * @since 1.4 + */ + @Parameter( property = "invoker.showVersion", defaultValue = "false" ) + private boolean showVersion; + + /** + * number of threads for running tests in parallel. This will be the number of maven forked process in parallel. + * + * @since 1.6 + */ + @Parameter( property = "invoker.parallelThreads", defaultValue = "1" ) + private int parallelThreads; + + /** + * @since 1.6 + */ + @Parameter( property = "plugin.artifacts", required = true, readonly = true ) + private List<Artifact> pluginArtifacts; + + /** + * If enable and if you have a settings file configured for the execution, it will be merged with your user + * settings. + * + * @since 1.6 + */ + @Parameter( property = "invoker.mergeUserSettings", defaultValue = "false" ) + private boolean mergeUserSettings; + + /** + * Additional environment variables to set on the command line. + * + * @since 1.8 + */ + @Parameter + private Map<String, String> environmentVariables; + + /** + * Additional variables for use in the hook scripts. + * + * @since 1.9 + */ + @Parameter + private Map<String, String> scriptVariables; + + /** + * The scripter runner that is responsible to execute hook scripts. + */ + private ScriptRunner scriptRunner; + + /** + * A string used to prefix the file name of the filtered POMs in case the POMs couldn't be filtered in-place (i.e. + * the projects were not cloned to a temporary directory), can be <code>null</code>. This will be set to + * <code>null</code> if the POMs have already been filtered during cloning. + */ + private String filteredPomPrefix = "interpolated-"; + + /** + * The format for elapsed build time. + */ + private final DecimalFormat secFormat = new DecimalFormat( "(0.0 s)", new DecimalFormatSymbols( Locale.ENGLISH ) ); + + /** + * The version of Maven which is used to run the builds + */ + private String actualMavenVersion; + + /** + * Invokes Maven on the configured test projects. + * + * @throws org.apache.maven.plugin.MojoExecutionException If the goal encountered severe errors. + * @throws org.apache.maven.plugin.MojoFailureException If any of the Maven builds failed. + */ + public void execute() + throws MojoExecutionException, MojoFailureException + { + if ( skipInvocation ) + { + getLog().info( "Skipping invocation per configuration." + + " If this is incorrect, ensure the skipInvocation parameter is not set to true." ); + return; + } + + if ( StringUtils.isEmpty( encoding ) ) + { + getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING + + ", i.e. build is platform dependent!" ); + } + + // done it here to prevent issues with concurrent access in case of parallel run + if ( !disableReports ) + { + setupReportsFolder(); + } + + BuildJob[] buildJobs; + if ( pom == null ) + { + try + { + buildJobs = getBuildJobs(); + } + catch ( final IOException e ) + { + throw new MojoExecutionException( "Error retrieving POM list from includes, " + + "excludes, and projects directory. Reason: " + e.getMessage(), e ); + } + } + else + { + try + { + projectsDirectory = pom.getCanonicalFile().getParentFile(); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to discover projectsDirectory from " + + "pom File parameter. Reason: " + e.getMessage(), e ); + } + + buildJobs = new BuildJob[] { new BuildJob( pom.getName(), BuildJob.Type.NORMAL ) }; + } + + if ( ( buildJobs == null ) || ( buildJobs.length < 1 ) ) + { + doFailIfNoProjects(); + + getLog().info( "No projects were selected for execution." ); + return; + } + + handleScriptRunnerWithScriptClassPath(); + + Collection<String> collectedProjects = new LinkedHashSet<String>(); + for ( BuildJob buildJob : buildJobs ) + { + collectProjects( projectsDirectory, buildJob.getProject(), collectedProjects, true ); + } + + File projectsDir = projectsDirectory; + + if ( cloneProjectsTo != null ) + { + cloneProjects( collectedProjects ); + projectsDir = cloneProjectsTo; + } + else + { + getLog().warn( "Filtering of parent/child POMs is not supported without cloning the projects" ); + } + + // First run setup jobs. + BuildJob[] setupBuildJobs = null; + try + { + setupBuildJobs = getSetupBuildJobsFromFolders(); + } + catch ( IOException e ) + { + getLog().error( "Failure during scanning of folders.", e ); + // TODO: Check shouldn't we fail in case of problems? + } + + if ( setupBuildJobs != null ) + { + // Run setup jobs in single thread + // mode. + // + // Some Idea about ordering? + getLog().info( "Running Setup Jobs" ); + runBuilds( projectsDir, setupBuildJobs, 1 ); + } + + // Afterwards run all other jobs. + BuildJob[] nonSetupBuildJobs = getNonSetupJobs( buildJobs ); + // We will run the non setup jobs with the configured + // parallelThreads number. + runBuilds( projectsDir, nonSetupBuildJobs, parallelThreads ); + + writeSummaryFile( nonSetupBuildJobs ); + + processResults( new InvokerSession( nonSetupBuildJobs ) ); + + } + + /** + * This will create the necessary folders for the reports. + * + * @throws MojoExecutionException in case of failure during creation of the reports folder. + */ + private void setupReportsFolder() + throws MojoExecutionException + { + // If it exists from previous run... + if ( reportsDirectory.exists() ) + { + try + { + FileUtils.deleteDirectory( reportsDirectory ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failure while trying to delete " + + reportsDirectory.getAbsolutePath(), e ); + } + } + if ( !reportsDirectory.mkdirs() ) + { + throw new MojoExecutionException( "Failure while creating the " + reportsDirectory.getAbsolutePath() ); + } + } + + private BuildJob[] getNonSetupJobs( BuildJob[] buildJobs ) + { + List<BuildJob> result = new LinkedList<BuildJob>(); + for ( int i = 0; i < buildJobs.length; i++ ) + { + if ( !buildJobs[i].getType().equals( BuildJob.Type.SETUP ) ) + { + result.add( buildJobs[i] ); + } + } + BuildJob[] buildNonSetupJobs = result.toArray( new BuildJob[result.size()] ); + return buildNonSetupJobs; + } + + private void handleScriptRunnerWithScriptClassPath() + { + final List<String> scriptClassPath; + if ( addTestClassPath ) + { + scriptClassPath = new ArrayList<String>( testClassPath ); + for ( Artifact pluginArtifact : pluginArtifacts ) + { + scriptClassPath.remove( pluginArtifact.getFile().getAbsolutePath() ); + } + } + else + { + scriptClassPath = null; + } + scriptRunner = new ScriptRunner( getLog() ); + scriptRunner.setScriptEncoding( encoding ); + scriptRunner.setGlobalVariable( "localRepositoryPath", localRepositoryPath ); + if ( scriptVariables != null ) + { + for ( Entry<String, String> entry : scriptVariables.entrySet() ) + { + scriptRunner.setGlobalVariable( entry.getKey(), entry.getValue() ); + } + } + scriptRunner.setClassPath( scriptClassPath ); + } + + private void writeSummaryFile( BuildJob[] buildJobs ) + throws MojoExecutionException + { + + File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" ); + + try + { + Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ); + + for ( int i = 0; i < buildJobs.length; i++ ) + { + BuildJob buildJob = buildJobs[i]; + if ( !buildJob.getResult().equals( BuildJob.Result.SUCCESS ) ) + { + writer.append( buildJob.getResult() ); + writer.append( " [" ); + writer.append( buildJob.getProject() ); + writer.append( "] " ); + if ( buildJob.getFailureMessage() != null ) + { + writer.append( " " ); + writer.append( buildJob.getFailureMessage() ); + } + writer.append( "\n" ); + } + } + + writer.close(); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to write summary report " + summaryReportFile, e ); + } + } + + protected void doFailIfNoProjects() + throws MojoFailureException + { + // should only be used during run and verify + } + + /** + * Processes the results of invoking the build jobs. + * + * @param invokerSession The session with the build jobs, must not be <code>null</code>. + * @throws MojoFailureException If the mojo had failed as a result of invoking the build jobs. + * @since 1.4 + */ + abstract void processResults( InvokerSession invokerSession ) + throws MojoFailureException; + + /** + * Creates a new reader for the specified file, using the plugin's {@link #encoding} parameter. + * + * @param file The file to create a reader for, must not be <code>null</code>. + * @return The reader for the file, never <code>null</code>. + * @throws java.io.IOException If the specified file was not found or the configured encoding is not supported. + */ + private Reader newReader( File file ) + throws IOException + { + if ( StringUtils.isNotEmpty( encoding ) ) + { + return ReaderFactory.newReader( file, encoding ); + } + else + { + return ReaderFactory.newPlatformReader( file ); + } + } + + /** + * Collects all projects locally reachable from the specified project. The method will as such try to read the POM + * and recursively follow its parent/module elements. + * + * @param projectsDir The base directory of all projects, must not be <code>null</code>. + * @param projectPath The relative path of the current project, can denote either the POM or its base directory, + * must not be <code>null</code>. + * @param projectPaths The set of already collected projects to add new projects to, must not be <code>null</code>. + * This set will hold the relative paths to either a POM file or a project base directory. + * @param included A flag indicating whether the specified project has been explicitly included via the parameter + * {@link #pomIncludes}. Such projects will always be added to the result set even if there is no + * corresponding POM. + * @throws org.apache.maven.plugin.MojoExecutionException If the project tree could not be traversed. + */ + private void collectProjects( File projectsDir, String projectPath, Collection<String> projectPaths, + boolean included ) + throws MojoExecutionException + { + projectPath = projectPath.replace( '\\', '/' ); + File pomFile = new File( projectsDir, projectPath ); + if ( pomFile.isDirectory() ) + { + pomFile = new File( pomFile, "pom.xml" ); + if ( !pomFile.exists() ) + { + if ( included ) + { + projectPaths.add( projectPath ); + } + return; + } + if ( !projectPath.endsWith( "/" ) ) + { + projectPath += '/'; + } + projectPath += "pom.xml"; + } + else if ( !pomFile.isFile() ) + { + return; + } + if ( !projectPaths.add( projectPath ) ) + { + return; + } + getLog().debug( "Collecting parent/child projects of " + projectPath ); + + Model model = PomUtils.loadPom( pomFile ); + + try + { + String projectsRoot = projectsDir.getCanonicalPath(); + String projectDir = pomFile.getParent(); + + String parentPath = "../pom.xml"; + if ( model.getParent() != null && StringUtils.isNotEmpty( model.getParent().getRelativePath() ) ) + { + parentPath = model.getParent().getRelativePath(); + } + String parent = relativizePath( new File( projectDir, parentPath ), projectsRoot ); + if ( parent != null ) + { + collectProjects( projectsDir, parent, projectPaths, false ); + } + + Collection<String> modulePaths = new LinkedHashSet<String>(); + + modulePaths.addAll( model.getModules() ); + + for ( Profile profile : model.getProfiles() ) + { + modulePaths.addAll( profile.getModules() ); + } + + for ( String modulePath : modulePaths ) + { + String module = relativizePath( new File( projectDir, modulePath ), projectsRoot ); + if ( module != null ) + { + collectProjects( projectsDir, module, projectPaths, false ); + } + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to analyze POM: " + pomFile, e ); + } + } + + /** + * Copies the specified projects to the directory given by {@link #cloneProjectsTo}. A project may either be denoted + * by a path to a POM file or merely by a path to a base directory. During cloning, the POM files will be filtered. + * + * @param projectPaths The paths to the projects to clone, relative to the projects directory, must not be + * <code>null</code> nor contain <code>null</code> elements. + * @throws org.apache.maven.plugin.MojoExecutionException If the the projects could not be copied/filtered. + */ + private void cloneProjects( Collection<String> projectPaths ) + throws MojoExecutionException + { + if ( !cloneProjectsTo.mkdirs() && cloneClean ) + { + try + { + FileUtils.cleanDirectory( cloneProjectsTo ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not clean the cloneProjectsTo directory. Reason: " + + e.getMessage(), e ); + } + } + + // determine project directories to clone + Collection<String> dirs = new LinkedHashSet<String>(); + for ( String projectPath : projectPaths ) + { + if ( !new File( projectsDirectory, projectPath ).isDirectory() ) + { + projectPath = getParentPath( projectPath ); + } + dirs.add( projectPath ); + } + + boolean filter; + + // clone project directories + try + { + filter = !cloneProjectsTo.getCanonicalFile().equals( projectsDirectory.getCanonicalFile() ); + + List<String> clonedSubpaths = new ArrayList<String>(); + + for ( String subpath : dirs ) + { + // skip this project if its parent directory is also scheduled for cloning + if ( !".".equals( subpath ) && dirs.contains( getParentPath( subpath ) ) ) + { + continue; + } + + // avoid copying subdirs that are already cloned. + if ( !alreadyCloned( subpath, clonedSubpaths ) ) + { + // avoid creating new files that point to dir/. + if ( ".".equals( subpath ) ) + { + String cloneSubdir = relativizePath( cloneProjectsTo, projectsDirectory.getCanonicalPath() ); + + // avoid infinite recursion if the cloneTo path is a subdirectory. + if ( cloneSubdir != null ) + { + File temp = File.createTempFile( "pre-invocation-clone.", "" ); + temp.delete(); + temp.mkdirs(); + + copyDirectoryStructure( projectsDirectory, temp ); + + FileUtils.deleteDirectory( new File( temp, cloneSubdir ) ); + + copyDirectoryStructure( temp, cloneProjectsTo ); + } + else + { + copyDirectoryStructure( projectsDirectory, cloneProjectsTo ); + } + } + else + { + File srcDir = new File( projectsDirectory, subpath ); + File dstDir = new File( cloneProjectsTo, subpath ); + copyDirectoryStructure( srcDir, dstDir ); + } + + clonedSubpaths.add( subpath ); + } + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to clone projects from: " + projectsDirectory + " to: " + + cloneProjectsTo + ". Reason: " + e.getMessage(), e ); + } + + // filter cloned POMs + if ( filter ) + { + for ( String projectPath : projectPaths ) + { + File pomFile = new File( cloneProjectsTo, projectPath ); + if ( pomFile.isFile() ) + { + buildInterpolatedFile( pomFile, pomFile ); + } + + // MINVOKER-186 + // The following is a temporary solution to support Maven 3.3.1 (.mvn/extensions.xml) filtering + // Will be replaced by MINVOKER-117 with general filtering mechanism + File baseDir = pomFile.getParentFile(); + File mvnDir = new File( baseDir, ".mvn" ); + if ( mvnDir.isDirectory() ) + { + File extensionsFile = new File( mvnDir, "extensions.xml" ); + if ( extensionsFile.isFile() ) + { + buildInterpolatedFile( extensionsFile, extensionsFile ); + } + } + // END MINVOKER-186 + } + filteredPomPrefix = null; + } + } + + /** + * Gets the parent path of the specified relative path. + * + * @param path The relative path whose parent should be retrieved, must not be <code>null</code>. + * @return The parent path or "." if the specified path has no parent, never <code>null</code>. + */ + private String getParentPath( String path ) + { + int lastSep = Math.max( path.lastIndexOf( '/' ), path.lastIndexOf( '\\' ) ); + return ( lastSep < 0 ) ? "." : path.substring( 0, lastSep ); + } + + /** + * Copied a directory structure with default exclusions (.svn, CVS, etc) + * + * @param sourceDir The source directory to copy, must not be <code>null</code>. + * @param destDir The target directory to copy to, must not be <code>null</code>. + * @throws java.io.IOException If the directory structure could not be copied. + */ + private void copyDirectoryStructure( File sourceDir, File destDir ) + throws IOException + { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( sourceDir ); + if ( !cloneAllFiles ) + { + scanner.addDefaultExcludes(); + } + scanner.scan(); + + /* + * NOTE: Make sure the destination directory is always there (even if empty) to support POM-less ITs. + */ + destDir.mkdirs(); + // Create all the directories, including any symlinks present in source + FileUtils.mkDirs( sourceDir, scanner.getIncludedDirectories(), destDir ); + + for ( String includedFile : scanner.getIncludedFiles() ) + { + File sourceFile = new File( sourceDir, includedFile ); + File destFile = new File( destDir, includedFile ); + FileUtils.copyFile( sourceFile, destFile ); + + // ensure clone project must be writable for additional changes + destFile.setWritable( true ); + } + } + + /** + * Determines whether the specified sub path has already been cloned, i.e. whether one of its ancestor directories + * was already cloned. + * + * @param subpath The sub path to check, must not be <code>null</code>. + * @param clonedSubpaths The list of already cloned paths, must not be <code>null</code> nor contain + * <code>null</code> elements. + * @return <code>true</code> if the specified path has already been cloned, <code>false</code> otherwise. + */ + static boolean alreadyCloned( String subpath, List<String> clonedSubpaths ) + { + for ( String path : clonedSubpaths ) + { + if ( ".".equals( path ) || subpath.equals( path ) || subpath.startsWith( path + File.separator ) ) + { + return true; + } + } + + return false; + } + + /** + * Runs the specified build jobs. + * + * @param projectsDir The base directory of all projects, must not be <code>null</code>. + * @param buildJobs The build jobs to run must not be <code>null</code> nor contain <code>null</code> elements. + * @throws org.apache.maven.plugin.MojoExecutionException If any build could not be launched. + */ + private void runBuilds( final File projectsDir, BuildJob[] buildJobs, int runWithParallelThreads ) + throws MojoExecutionException + { + if ( !localRepositoryPath.exists() ) + { + localRepositoryPath.mkdirs(); + } + + // ----------------------------------------------- + // interpolate settings file + // ----------------------------------------------- + + File interpolatedSettingsFile = interpolateSettings(); + + final File mergedSettingsFile = mergeSettings( interpolatedSettingsFile ); + + if ( mavenHome != null ) + { + actualMavenVersion = SelectorUtils.getMavenVersion( mavenHome ); + } + else + { + actualMavenVersion = SelectorUtils.getMavenVersion(); + } + scriptRunner.setGlobalVariable( "mavenVersion", actualMavenVersion ); + + final CharSequence actualJreVersion; + // @todo if ( javaVersions ) ... to be picked up from toolchains + if ( javaHome != null ) + { + actualJreVersion = resolveExternalJreVersion(); + } + else + { + actualJreVersion = SelectorUtils.getJreVersion(); + } + + try + { + if ( runWithParallelThreads > 1 ) + { + getLog().info( "use parallelThreads " + runWithParallelThreads ); + + ExecutorService executorService = Executors.newFixedThreadPool( runWithParallelThreads ); + for ( final BuildJob job : buildJobs ) + { + executorService.execute( new Runnable() + { + public void run() + { + try + { + runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion ); + } + catch ( MojoExecutionException e ) + { + throw new RuntimeException( e.getMessage(), e ); + } + } + } ); + } + + try + { + executorService.shutdown(); + // TODO add a configurable time out + executorService.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS ); + } + catch ( InterruptedException e ) + { + throw new MojoExecutionException( e.getMessage(), e ); + } + } + else + { + for ( BuildJob job : buildJobs ) + { + runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion ); + } + } + } + finally + { + if ( interpolatedSettingsFile != null && cloneProjectsTo == null ) + { + interpolatedSettingsFile.delete(); + } + if ( mergedSettingsFile != null && mergedSettingsFile.exists() ) + { + mergedSettingsFile.delete(); + } + } + } + + /** + * Interpolate settings.xml file. + * + * @return The interpolated settings.xml file. + * @throws MojoExecutionException in case of a problem. + */ + private File interpolateSettings() + throws MojoExecutionException + { + File interpolatedSettingsFile = null; + if ( settingsFile != null ) + { + if ( cloneProjectsTo != null ) + { + interpolatedSettingsFile = new File( cloneProjectsTo, "interpolated-" + settingsFile.getName() ); + } + else + { + interpolatedSettingsFile = + new File( settingsFile.getParentFile(), "interpolated-" + settingsFile.getName() ); + } + buildInterpolatedFile( settingsFile, interpolatedSettingsFile ); + } + return interpolatedSettingsFile; + } + + /** + * Merge the settings file + * + * @param interpolatedSettingsFile The interpolated settings file. + * @return The merged settings file. + * @throws MojoExecutionException Fail the build in case the merged settings file can't be created. + */ + private File mergeSettings( File interpolatedSettingsFile ) + throws MojoExecutionException + { + File mergedSettingsFile; + Settings mergedSettings = this.settings; + if ( mergeUserSettings ) + { + if ( interpolatedSettingsFile != null ) + { + // Have to merge the specified settings file (dominant) and the one of the invoking Maven process + try + { + SettingsBuildingRequest request = new DefaultSettingsBuildingRequest(); + request.setGlobalSettingsFile( interpolatedSettingsFile ); + + Settings dominantSettings = settingsBuilder.build( request ).getEffectiveSettings(); + Settings recessiveSettings = cloneSettings(); + SettingsUtils.merge( dominantSettings, recessiveSettings, TrackableBase.USER_LEVEL ); + + mergedSettings = dominantSettings; + getLog().debug( "Merged specified settings file with settings of invoking process" ); + } + catch ( SettingsBuildingException e ) + { + throw new MojoExecutionException( "Could not read specified settings file", e ); + } + } + } + + if ( this.settingsFile != null && !mergeUserSettings ) + { + mergedSettingsFile = interpolatedSettingsFile; + } + else + { + try + { + mergedSettingsFile = writeMergedSettingsFile( mergedSettings ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Could not create temporary file for invoker settings.xml", e ); + } + } + return mergedSettingsFile; + } + + private File writeMergedSettingsFile( Settings mergedSettings ) + throws IOException + { + File mergedSettingsFile; + mergedSettingsFile = File.createTempFile( "invoker-settings", ".xml" ); + + SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer(); + + FileWriter fileWriter = null; + try + { + fileWriter = new FileWriter( mergedSettingsFile ); + settingsWriter.write( fileWriter, mergedSettings ); + fileWriter.close(); + fileWriter = null; + } + finally + { + IOUtil.close( fileWriter ); + } + + if ( getLog().isDebugEnabled() ) + { + getLog().debug( "Created temporary file for invoker settings.xml: " + + mergedSettingsFile.getAbsolutePath() ); + } + return mergedSettingsFile; + } + + private Settings cloneSettings() + { + Settings recessiveSettings = SettingsUtils.copySettings( this.settings ); + + // MINVOKER-133: reset sourceLevelSet + resetSourceLevelSet( recessiveSettings ); + for ( org.apache.maven.settings.Mirror mirror : recessiveSettings.getMirrors() ) + { + resetSourceLevelSet( mirror ); + } + for ( org.apache.maven.settings.Server server : recessiveSettings.getServers() ) + { + resetSourceLevelSet( server ); + } + for ( org.apache.maven.settings.Proxy proxy : recessiveSettings.getProxies() ) + { + resetSourceLevelSet( proxy ); + } + for ( org.apache.maven.settings.Profile profile : recessiveSettings.getProfiles() ) + { + resetSourceLevelSet( profile ); + } + + return recessiveSettings; + } + + private void resetSourceLevelSet( org.apache.maven.settings.TrackableBase trackable ) + { + try + { + ReflectionUtils.setVariableValueInObject( trackable, "sourceLevelSet", Boolean.FALSE ); + getLog().debug( "sourceLevelSet: " + + ReflectionUtils.getValueIncludingSuperclasses( "sourceLevelSet", trackable ) ); + } + catch ( IllegalAccessException e ) + { + // noop + } + } + + private CharSequence resolveExternalJreVersion() + { + Artifact pluginArtifact = mojoExecution.getMojoDescriptor().getPluginDescriptor().getPluginArtifact(); + pluginArtifact.getFile(); + + Commandline commandLine = new Commandline(); + commandLine.setExecutable( new File( javaHome, "bin/java" ).getAbsolutePath() ); + commandLine.createArg().setValue( "-cp" ); + commandLine.createArg().setFile( pluginArtifact.getFile() ); + commandLine.createArg().setValue( SystemPropertyPrinter.class.getName() ); + commandLine.createArg().setValue( "java.version" ); + + final StringBuilder actualJreVersion = new StringBuilder(); + StreamConsumer consumer = new StreamConsumer() + { + public void consumeLine( String line ) + { + actualJreVersion.append( line ); + } + }; + try + { + CommandLineUtils.executeCommandLine( commandLine, consumer, null ); + } + catch ( CommandLineException e ) + { + getLog().warn( e.getMessage() ); + } + return actualJreVersion; + } + + /** + * Interpolate the pom file. + * + * @param pomFile The pom file. + * @param basedir The base directory. + * @return interpolated pom file location in case we have interpolated the pom file otherwise the original pom file + * will be returned. + * @throws MojoExecutionException + */ + private File interpolatePomFile( File pomFile, File basedir ) + throws MojoExecutionException + { + File interpolatedPomFile = null; + if ( pomFile != null ) + { + if ( StringUtils.isNotEmpty( filteredPomPrefix ) ) + { + interpolatedPomFile = new File( basedir, filteredPomPrefix + pomFile.getName() ); + buildInterpolatedFile( pomFile, interpolatedPomFile ); + } + else + { + interpolatedPomFile = pomFile; + } + } + return interpolatedPomFile; + } + + /** + * Runs the specified project. + * + * @param projectsDir The base directory of all projects, must not be <code>null</code>. + * @param buildJob The build job to run, must not be <code>null</code>. + * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code> to use + * the current user settings. + * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched. + */ + private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome, + CharSequence actualJreVersion ) + throws MojoExecutionException + { + // FIXME: Think about the following code part -- START + File pomFile = new File( projectsDir, buildJob.getProject() ); + File basedir; + if ( pomFile.isDirectory() ) + { + basedir = pomFile; + pomFile = new File( basedir, "pom.xml" ); + if ( !pomFile.exists() ) + { + pomFile = null; + } + else + { + buildJob.setProject( buildJob.getProject() + File.separator + "pom.xml" ); + } + } + else + { + basedir = pomFile.getParentFile(); + } + + File interpolatedPomFile = interpolatePomFile( pomFile, basedir ); + // FIXME: Think about the following code part -- ^^^^^^^ END + + getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() ); + + InvokerProperties invokerProperties = getInvokerProperties( basedir ); + + // let's set what details we can + buildJob.setName( invokerProperties.getJobName() ); + buildJob.setDescription( invokerProperties.getJobDescription() ); + + try + { + int selection = getSelection( invokerProperties, actualJreVersion ); + if ( selection == 0 ) + { + long milliseconds = System.currentTimeMillis(); + boolean executed; + try + { + // CHECKSTYLE_OFF: LineLength + executed = + runBuild( basedir, interpolatedPomFile, settingsFile, actualJavaHome, invokerProperties ); + // CHECKSTYLE_ON: LineLength + } + finally + { + milliseconds = System.currentTimeMillis() - milliseconds; + buildJob.setTime( milliseconds / 1000.0 ); + } + + if ( executed ) + { + buildJob.setResult( BuildJob.Result.SUCCESS ); + + if ( !suppressSummaries ) + { + getLog().info( pad( buildJob ).success( "SUCCESS" ).a( ' ' ) + + formatTime( buildJob.getTime() ) ); + } + } + else + { + buildJob.setResult( BuildJob.Result.SKIPPED ); + + if ( !suppressSummaries ) + { + getLog().info( pad( buildJob ).warning( "SKIPPED" ).a( ' ' ) + + formatTime( buildJob.getTime() ) ); + } + } + } + else + { + buildJob.setResult( BuildJob.Result.SKIPPED ); + + StringBuilder message = new StringBuilder(); + if ( selection == Selector.SELECTOR_MULTI ) + { + message.append( "non-matching selectors" ); + } + else + { + if ( ( selection & Selector.SELECTOR_MAVENVERSION ) != 0 ) + { + message.append( "Maven version" ); + } + if ( ( selection & Selector.SELECTOR_JREVERSION ) != 0 ) + { + if ( message.length() > 0 ) + { + message.append( ", " ); + } + message.append( "JRE version" ); + } + if ( ( selection & Selector.SELECTOR_OSFAMILY ) != 0 ) + { + if ( message.length() > 0 ) + { + message.append( ", " ); + } + message.append( "OS" ); + } + } + + if ( !suppressSummaries ) + { + getLog().info( pad( buildJob ).warning( "SKIPPED" ) + " due to " + message.toString() ); + } + + // Abuse failureMessage, the field in the report which should contain the reason for skipping + // Consider skipCode + I18N + buildJob.setFailureMessage( "Skipped due to " + message.toString() ); + } + } + catch ( RunErrorException e ) + { + buildJob.setResult( BuildJob.Result.ERROR ); + buildJob.setFailureMessage( e.getMessage() ); + + if ( !suppressSummaries ) + { + getLog().info( pad( buildJob ).failure( "ERROR" ).a( ' ' ) + formatTime( buildJob.getTime() ) ); + getLog().info( " " + e.getMessage() ); + } + } + catch ( RunFailureException e ) + { + buildJob.setResult( e.getType() ); + buildJob.setFailureMessage( e.getMessage() ); + + if ( !suppressSummaries ) + { + getLog().info( pad( buildJob ).failure( "FAILED" ).a( ' ' ) + formatTime( buildJob.getTime() ) ); + getLog().info( " " + e.getMessage() ); + } + } + finally + { + deleteInterpolatedPomFile( interpolatedPomFile ); + writeBuildReport( buildJob ); + } + } + + private MessageBuilder pad( BuildJob buildJob ) + { + MessageBuilder buffer = buffer( 128 ); + + buffer.a( " " ); + buffer.a( buildJob.getProject() ); + buffer.a( ' ' ); + + int l = 11 + buildJob.getProject().length(); + + if ( l < RESULT_COLUMN ) + { + for ( int i = RESULT_COLUMN - l; i > 0; i-- ) + { + buffer.a( '.' ); + } + } + + return buffer.a( ' ' ); + } + + /** + * Delete the interpolated pom file if it has been created before. + * + * @param interpolatedPomFile The interpolated pom file. + */ + private void deleteInterpolatedPomFile( File interpolatedPomFile ) + { + if ( interpolatedPomFile != null && StringUtils.isNotEmpty( filteredPomPrefix ) ) + { + interpolatedPomFile.delete(); + } + } + + /** + * Determines whether selector conditions of the specified invoker properties match the current environment. + * + * @param invokerProperties The invoker properties to check, must not be <code>null</code>. + * @return <code>0</code> if the job corresponding to the properties should be run, otherwise a bitwise value + * representing the reason why it should be skipped. + */ + private int getSelection( InvokerProperties invokerProperties, CharSequence actualJreVersion ) + { + return new Selector( actualMavenVersion, actualJreVersion.toString() ).getSelection( invokerProperties ); + } + + /** + * Writes the XML report for the specified build job unless report generation has been disabled. + * + * @param buildJob The build job whose report should be written, must not be <code>null</code>. + * @throws org.apache.maven.plugin.MojoExecutionException If the report could not be written. + */ + private void writeBuildReport( BuildJob buildJob ) + throws MojoExecutionException + { + if ( disableReports ) + { + return; + } + + String safeFileName = buildJob.getProject().replace( '/', '_' ).replace( '\\', '_' ).replace( ' ', '_' ); + if ( safeFileName.endsWith( "_pom.xml" ) ) + { + safeFileName = safeFileName.substring( 0, safeFileName.length() - "_pom.xml".length() ); + } + + File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" ); + try + { + FileOutputStream fos = new FileOutputStream( reportFile ); + try + { + Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ); + BuildJobXpp3Writer writer = new BuildJobXpp3Writer(); + writer.write( osw, buildJob ); + osw.close(); + } + finally + { + fos.close(); + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to write build report " + reportFile, e ); + } + } + + /** + * Formats the specified build duration time. + * + * @param seconds The duration of the build. + * @return The formatted time, never <code>null</code>. + */ + private String formatTime( double seconds ) + { + return secFormat.format( seconds ); + } + + /** + * Runs the specified project. + * + * @param basedir The base directory of the project, must not be <code>null</code>. + * @param pomFile The (already interpolated) POM file, may be <code>null</code> for a POM-less Maven invocation. + * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code>. Will + * be merged with the settings file of the invoking Maven process. + * @param invokerProperties The properties to use. + * @return <code>true</code> if the project was launched or <code>false</code> if the selector script indicated that + * the project should be skipped. + * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched. + * @throws org.apache.maven.shared.scriptinterpreter.RunFailureException If either a hook script or the build itself + * failed. + */ + private boolean runBuild( File basedir, File pomFile, File settingsFile, File actualJavaHome, + InvokerProperties invokerProperties ) + throws MojoExecutionException, RunFailureException + { + if ( getLog().isDebugEnabled() && !invokerProperties.getProperties().isEmpty() ) + { + Properties props = invokerProperties.getProperties(); + getLog().debug( "Using invoker properties:" ); + for ( String key : new TreeSet<String>( props.stringPropertyNames() ) ) + { + String value = props.getProperty( key ); + getLog().debug( " " + key + " = " + value ); + } + } + + List<String> goals = getGoals( basedir ); + + List<String> profiles = getProfiles( basedir ); + + Map<String, Object> context = new LinkedHashMap<String, Object>(); + + FileLogger logger = setupBuildLogFile( basedir ); + try + { + try + { + scriptRunner.run( "selector script", basedir, selectorScript, context, logger, BuildJob.Result.SKIPPED, + false ); + } + catch ( RunErrorException e ) + { + throw e; + } + catch ( RunFailureException e ) + { + return false; + } + + scriptRunner.run( "pre-build script", basedir, preBuildHookScript, context, logger, + BuildJob.Result.FAILURE_PRE_HOOK, false ); + + final InvocationRequest request = new DefaultInvocationRequest(); + + request.setLocalRepositoryDirectory( localRepositoryPath ); + + request.setBatchMode( true ); + + request.setShowErrors( showErrors ); + + request.setDebug( debug ); + + request.setShowVersion( showVersion ); + + setupLoggerForBuildJob( logger, request ); + + if ( mavenHome != null ) + { + invoker.setMavenHome( mavenHome ); + // FIXME: Should we really take care of M2_HOME? + request.addShellEnvironment( "M2_HOME", mavenHome.getAbsolutePath() ); + } + + if ( mavenExecutable != null ) + { + invoker.setMavenExecutable( new File( mavenExecutable ) ); + } + + if ( actualJavaHome != null ) + { + request.setJavaHome( actualJavaHome ); + } + + if ( environmentVariables != null ) + { + for ( Map.Entry<String, String> variable : environmentVariables.entrySet() ) + { + request.addShellEnvironment( variable.getKey(), variable.getValue() ); + } + } + + for ( int invocationIndex = 1;; invocationIndex++ ) + { + if ( invocationIndex > 1 && !invokerProperties.isInvocationDefined( invocationIndex ) ) + { + break; + } + + request.setBaseDirectory( basedir ); + + request.setPomFile( pomFile ); + + request.setGoals( goals ); + + request.setProfiles( profiles ); + + request.setMavenOpts( mavenOpts ); + + request.setOffline( false ); + + request.setUserSettingsFile( settingsFile ); + + Properties systemProperties = + getSystemProperties( basedir, invokerProperties.getSystemPropertiesFile( invocationIndex ) ); + request.setProperties( systemProperties ); + + invokerProperties.configureInvocation( request, invocationIndex ); + + if ( getLog().isDebugEnabled() ) + { + try + { + getLog().debug( "Using MAVEN_OPTS: " + request.getMavenOpts() ); + getLog().debug( "Executing: " + new MavenCommandLineBuilder().build( request ) ); + } + catch ( CommandLineConfigurationException e ) + { + getLog().debug( "Failed to display command line: " + e.getMessage() ); + } + } + + InvocationResult result; + + try + { + result = invoker.execute( request ); + } + catch ( final MavenInvocationException e ) + { + getLog().debug( "Error invoking Maven: " + e.getMessage(), e ); + throw new RunFailureException( "Maven invocation failed. " + e.getMessage(), + BuildJob.Result.FAILURE_BUILD ); + } + + verify( result, invocationIndex, invokerProperties, logger ); + } + + scriptRunner.run( "post-build script", basedir, postBuildHookScript, context, logger, + BuildJob.Result.FAILURE_POST_HOOK, true ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( e.getMessage(), e ); + } + finally + { + if ( logger != null ) + { + logger.close(); + } + } + return true; + } + + private void setupLoggerForBuildJob( FileLogger logger, final InvocationRequest request ) + { + if ( logger != null ) + { + request.setErrorHandler( logger ); + + request.setOutputHandler( logger ); + } + } + + /** + * Initializes the build logger for the specified project. This will write the logging information into + * {@code build.log}. + * + * @param basedir The base directory of the project, must not be <code>null</code>. + * @return The build logger or <code>null</code> if logging has been disabled. + * @throws org.apache.maven.plugin.MojoExecutionException If the log file could not be created. + */ + private FileLogger setupBuildLogFile( File basedir ) + throws MojoExecutionException + { + FileLogger logger = null; + + if ( !noLog ) + { + File outputLog = new File( basedir, "build.log" ); + try + { + if ( streamLogs ) + { + logger = new FileLogger( outputLog, getLog() ); + } + else + { + logger = new FileLogger( outputLog ); + } + + getLog().debug( "Build log initialized in: " + outputLog ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error initializing build logfile in: " + outputLog, e ); + } + } + + return logger; + } + + /** + * Gets the system properties to use for the specified project. + * + * @param basedir The base directory of the project, must not be <code>null</code>. + * @param filename The filename to the properties file to load, may be <code>null</code> to use the default path + * given by {@link #testPropertiesFile}. + * @return The system properties to use, may be empty but never <code>null</code>. + * @throws org.apache.maven.plugin.MojoExecutionException If the properties file exists but could not be read. + */ + private Properties getSystemProperties( final File basedir, final String filename ) + throws MojoExecutionException + { + Properties collectedTestProperties = new Properties(); + + if ( properties != null ) + { + // MINVOKER-118: property can have empty value, which is not accepted by collectedTestProperties + for ( Map.Entry<String, String> entry : properties.entrySet() ) + { + if ( entry.getValue() != null ) + { + collectedTestProperties.put( entry.getKey(), entry.getValue() ); + } + } + } + + File propertiesFile = null; + if ( filename != null ) + { + propertiesFile = new File( basedir, filename ); + } + else if ( testPropertiesFile != null ) + { + propertiesFile = new File( basedir, testPropertiesFile ); + } + + if ( propertiesFile != null && propertiesFile.isFile() ) + { + InputStream fin = null; + try + { + fin = new FileInputStream( propertiesFile ); + + Properties loadedProperties = new Properties(); + loadedProperties.load( fin ); + fin.close(); + fin = null; + collectedTestProperties.putAll( loadedProperties ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Error reading system properties from " + propertiesFile ); + } + finally + { + IOUtil.close( fin ); + } + } + + return collectedTestProperties; + } + + /** + * Verifies the invocation result. + * + * @param result The invocation result to check, must not be <code>null</code>. + * @param invocationIndex The index of the invocation for which to check the exit code, must not be negative. + * @param invokerProperties The invoker properties used to check the exit code, must not be <code>null</code>. + * @param logger The build logger, may be <code>null</code> if logging is disabled. + * @throws org.apache.maven.shared.scriptinterpreter.RunFailureException If the invocation result indicates a build + * failure. + */ + private void verify( InvocationResult result, int invocationIndex, InvokerProperties invokerProperties, + FileLogger logger ) + throws RunFailureException + { + if ( result.getExecutionException() != null ) + { + throw new RunFailureException( "The Maven invocation failed. " + + result.getExecutionException().getMessage(), BuildJob.Result.ERROR ); + } + else if ( !invokerProperties.isExpectedResult( result.getExitCode(), invocationIndex ) ) + { + StringBuilder buffer = new StringBuilder( 256 ); + buffer.append( "The build exited with code " ).append( result.getExitCode() ).append( ". " ); + if ( logger != null ) + { + buffer.append( "See " ); + buffer.append( logger.getOutputFile().getAbsolutePath() ); + buffer.append( " for details." ); + } + else + { + buffer.append( "See console output for details." ); + } + throw new RunFailureException( buffer.toString(), BuildJob.Result.FAILURE_BUILD ); + } + } + + /** + * Gets the goal list for the specified project. + * + * @param basedir The base directory of the project, must not be <code>null</code>. + * @return The list of goals to run when building the project, may be empty but never <code>null</code>. + * @throws org.apache.maven.plugin.MojoExecutionException If the profile file could not be read. + */ + List<String> getGoals( final File basedir ) + throws MojoExecutionException + { + try + { + // FIXME: Currently we have null for goalsFile which has been removed. + // This might mean we can remove getGoals() at all ? Check this. + return getTokens( basedir, null, goals ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "error reading goals", e ); + } + } + + /** + * Gets the profile list for the specified project. + * + * @param basedir The base directory of the project, must not be <code>null</code>. + * @return The list of profiles to activate when building the project, may be empty but never <code>null</code>. + * @throws org.apache.maven.plugin.MojoExecutionException If the profile file could not be read. + */ + List<String> getProfiles( File basedir ) + throws MojoExecutionException + { + try + { + return getTokens( basedir, null, profiles ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "error reading profiles", e ); + } + } + + private List<String> calculateExcludes() + throws IOException + { + List<String> excludes = + ( pomExcludes != null ) ? new ArrayList<String>( pomExcludes ) : new ArrayList<String>(); + if ( this.settingsFile != null ) + { + String exclude = relativizePath( this.settingsFile, projectsDirectory.getCanonicalPath() ); + if ( exclude != null ) + { + excludes.add( exclude.replace( '\\', '/' ) ); + getLog().debug( "Automatically excluded " + exclude + " from project scanning" ); + } + } + return excludes; + + } + + /** + * @return The list of setupUp jobs. + * @throws IOException + * @see {@link #setupIncludes} + */ + private BuildJob[] getSetupBuildJobsFromFolders() + throws IOException + { + List<String> excludes = calculateExcludes(); + + BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); + if ( getLog().isDebugEnabled() ) + { + getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); + } + + return setupPoms; + } + + /** + * Gets the build jobs that should be processed. Note that the order of the returned build jobs is significant. + * + * @return The build jobs to process, may be empty but never <code>null</code>. + * @throws java.io.IOException If the projects directory could not be scanned. + */ + BuildJob[] getBuildJobs() + throws IOException + { + BuildJob[] buildJobs; + + if ( invokerTest == null ) + { + List<String> excludes = calculateExcludes(); + + BuildJob[] setupPoms = scanProjectsDirectory( setupIncludes, excludes, BuildJob.Type.SETUP ); + if ( getLog().isDebugEnabled() ) + { + getLog().debug( "Setup projects: " + Arrays.asList( setupPoms ) ); + } + + BuildJob[] normalPoms = scanProjectsDirectory( pomIncludes, excludes, BuildJob.Type.NORMAL ); + + Map<String, BuildJob> uniquePoms = new LinkedHashMap<String, BuildJob>(); + for ( BuildJob setupPom : setupPoms ) + { + uniquePoms.put( setupPom.getProject(), setupPom ); + } + for ( BuildJob normalPom : normalPoms ) + { + if ( !uniquePoms.containsKey( normalPom.getProject() ) ) + { + uniquePoms.put( normalPom.getProject(), normalPom ); + } + } + + buildJobs = uniquePoms.values().toArray( new BuildJob[uniquePoms.size()] ); + } + else + { + String[] testRegexes = StringUtils.split( invokerTest, "," ); + List<String> includes = new ArrayList<String>( testRegexes.length ); + List<String> excludes = new ArrayList<String>(); + + for ( String regex : testRegexes ) + { + // user just use -Dinvoker.test=MWAR191,MNG111 to use a directory thats the end is not pom.xml + if ( regex.startsWith( "!" ) ) + { + excludes.add( regex.substring( 1 ) ); + } + else + { + includes.add( regex ); + } + } + + // it would be nice if we could figure out what types these are... but perhaps + // not necessary for the -Dinvoker.test=xxx t + buildJobs = scanProjectsDirectory( includes, excludes, BuildJob.Type.DIRECT ); + } + + relativizeProjectPaths( buildJobs ); + + return buildJobs; + } + + /** + * Scans the projects directory for projects to build. Both (POM) files and mere directories will be matched by the + * scanner patterns. If the patterns match a directory which contains a file named "pom.xml", the results will + * include the path to this file rather than the directory path in order to avoid duplicate invocations of the same + * project. + * + * @param includes The include patterns for the scanner, may be <code>null</code>. + * @param excludes The exclude patterns for the scanner, may be <code>null</code> to exclude nothing. + * @param type The type to assign to the resulting build jobs, must not be <code>null</code>. + * @return The build jobs matching the patterns, never <code>null</code>. + * @throws java.io.IOException If the project directory could not be scanned. + */ + private BuildJob[] scanProjectsDirectory( List<String> includes, List<String> excludes, String type ) + throws IOException + { + if ( !projectsDirectory.isDirectory() ) + { + return new BuildJob[0]; + } + + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir( projectsDirectory.getCanonicalFile() ); + scanner.setFollowSymlinks( false ); + if ( includes != null ) + { + scanner.setIncludes( includes.toArray( new String[includes.size()] ) ); + } + if ( excludes != null ) + { + scanner.setExcludes( excludes.toArray( new String[excludes.size()] ) ); + } + scanner.addDefaultExcludes(); + scanner.scan(); + + Map<String, BuildJob> matches = new LinkedHashMap<String, BuildJob>(); + + for ( String includedFile : scanner.getIncludedFiles() ) + { + matches.put( includedFile, new BuildJob( includedFile, type ) ); + } + + for ( String includedDir : scanner.getIncludedDirectories() ) + { + String includedFile = includedDir + File.separatorChar + "pom.xml"; + if ( new File( scanner.getBasedir(), includedFile ).isFile() ) + { + matches.put( includedFile, new BuildJob( includedFile, type ) ); + } + else + { + matches.put( includedDir, new BuildJob( includedDir, type ) ); + } + } + + return matches.values().toArray( new BuildJob[matches.size()] ); + } + + /** + * Relativizes the project paths of the specified build jobs against the directory specified by + * {@link #projectsDirectory} (if possible). If a project path does not denote a sub path of the projects directory, + * it is returned as is. + * + * @param buildJobs The build jobs whose project paths should be relativized, must not be <code>null</code> nor + * contain <code>null</code> elements. + * @throws java.io.IOException If any path could not be relativized. + */ + private void relativizeProjectPaths( BuildJob[] buildJobs ) + throws IOException + { + String projectsDirPath = projectsDirectory.getCanonicalPath(); + + for ( BuildJob buildJob : buildJobs ) + { + String projectPath = buildJob.getProject(); + + File file = new File( projectPath ); + + if ( !file.isAbsolute() ) + { + file = new File( projectsDirectory, projectPath ); + } + + String relativizedPath = relativizePath( file, projectsDirPath ); + + if ( relativizedPath == null ) + { + relativizedPath = projectPath; + } + + buildJob.setProject( relativizedPath ); + } + } + + /** + * Relativizes the specified path against the given base directory. Besides relativization, the returned path will + * also be normalized, e.g. directory references like ".." will be removed. + * + * @param path The path to relativize, must not be <code>null</code>. + * @param basedir The (canonical path of the) base directory to relativize against, must not be <code>null</code>. + * @return The relative path in normal form or <code>null</code> if the input path does not denote a sub path of the + * base directory. + * @throws java.io.IOException If the path could not be relativized. + */ + private String relativizePath( File path, String basedir ) + throws IOException + { + String relativizedPath = path.getCanonicalPath(); + + if ( relativizedPath.startsWith( basedir ) ) + { + relativizedPath = relativizedPath.substring( basedir.length() ); + if ( relativizedPath.startsWith( File.separator ) ) + { + relativizedPath = relativizedPath.substring( File.separator.length() ); + } + + return relativizedPath; + } + else + { + return null; + } + } + + /** + * Returns the map-based value source used to interpolate POMs and other stuff. + * + * @param escapeXml {@code true}, to escape any XML special characters in the property values; {@code false}, to not + * escape any property values. + * + * @return The map-based value source for interpolation, never <code>null</code>. + */ + private Map<String, Object> getInterpolationValueSource( final boolean escapeXml ) + { + Map<String, Object> props = new HashMap<String, Object>(); + + if ( filterProperties != null ) + { + props.putAll( filterProperties ); + } + props.put( "basedir", this.project.getBasedir().getAbsolutePath() ); + props.put( "baseurl", toUrl( this.project.getBasedir().getAbsolutePath() ) ); + if ( settings.getLocalRepository() != null ) + { + props.put( "localRepository", settings.getLocalRepository() ); + props.put( "localRepositoryUrl", toUrl( settings.getLocalRepository() ) ); + } + + return new CompositeMap( this.project, props, escapeXml ); + } + + /** + * Converts the specified filesystem path to a URL. The resulting URL has no trailing slash regardless whether the + * path denotes a file or a directory. + * + * @param filename The filesystem path to convert, must not be <code>null</code>. + * @return The <code>file:</code> URL for the specified path, never <code>null</code>. + */ + private static String toUrl( String filename ) + { + /* + * NOTE: Maven fails to properly handle percent-encoded "file:" URLs (WAGON-111) so don't use File.toURI() here + * as-is but use the decoded path component in the URL. + */ + String url = "file://" + new File( filename ).toURI().getPath(); + if ( url.endsWith( "/" ) ) + { + url = url.substring( 0, url.length() - 1 ); + } + return url; + } + + /** + * Gets goal/profile names for the specified project, either directly from the plugin configuration or from an + * external token file. + * + * @param basedir The base directory of the test project, must not be <code>null</code>. + * @param filename The (simple) name of an optional file in the project base directory from which to read + * goals/profiles, may be <code>null</code>. + * @param defaultTokens The list of tokens to return in case the specified token file does not exist, may be + * <code>null</code>. + * @return The list of goal/profile names, may be empty but never <code>null</code>. + * @throws java.io.IOException If the token file exists but could not be parsed. + */ + private List<String> getTokens( File basedir, String filename, List<String> defaultTokens ) + throws IOException + { + List<String> tokens = ( defaultTokens != null ) ? defaultTokens : new ArrayList<String>(); + + if ( StringUtils.isNotEmpty( filename ) ) + { + File tokenFile = new File( basedir, filename ); + + if ( tokenFile.exists() ) + { + tokens = readTokens( tokenFile ); + } + } + + return tokens; + } + + /** + * Reads the tokens from the specified file. Tokens are separated either by line terminators or commas. During + * parsing, the file contents will be interpolated. + * + * @param tokenFile The file to read the tokens from, must not be <code>null</code>. + * @return The list of tokens, may be empty but never <code>null</code>. + * @throws java.io.IOException If the token file could not be read. + */ + private List<String> readTokens( final File tokenFile ) + throws IOException + { + List<String> result = new ArrayList<String>(); + + BufferedReader reader = null; + try + { + Map<String, Object> composite = getInterpolationValueSource( false ); + reader = new BufferedReader( new InterpolationFilterReader( newReader( tokenFile ), composite ) ); + + for ( String line = reader.readLine(); line != null; line = reader.readLine() ) + { + result.addAll( collectListFromCSV( line ) ); + } + + reader.close(); + reader = null; + } + finally + { + IOUtil.close( reader ); + } + + return result; + } + + /** + * Gets a list of comma separated tokens from the specified line. + * + * @param csv The line with comma separated tokens, may be <code>null</code>. + * @return The list of tokens from the line, may be empty but never <code>null</code>. + */ + private List<String> collectListFromCSV( final String csv ) + { + final List<String> result = new ArrayList<String>(); + + if ( ( csv != null ) && ( csv.trim().length() > 0 ) ) + { + final StringTokenizer st = new StringTokenizer( csv, "," ); + + while ( st.hasMoreTokens() ) + { + result.add( st.nextToken().trim() ); + } + } + + return result; + } + + /** + * Interpolates the specified POM/settings file to a temporary file. The destination file may be same as the input + * file, i.e. interpolation can be performed in-place. + * <p> + * <b>Note:</b>This methods expects the file to be a XML file and applies special XML escaping during interpolation. + * </p> + * + * @param originalFile The XML file to interpolate, must not be <code>null</code>. + * @param interpolatedFile The target file to write the interpolated contents of the original file to, must not be + * <code>null</code>. + * + * @throws org.apache.maven.plugin.MojoExecutionException If the target file could not be created. + */ + void buildInterpolatedFile( File originalFile, File interpolatedFile ) + throws MojoExecutionException + { + getLog().debug( "Interpolate " + originalFile.getPath() + " to " + interpolatedFile.getPath() ); + + try + { + String xml; + + Reader reader = null; + try + { + // interpolation with token @...@ + Map<String, Object> composite = getInterpolationValueSource( true ); + reader = + new InterpolationFilterReader( ReaderFactory.newXmlReader( originalFile ), composite, "@", "@" ); + + xml = IOUtil.toString( reader ); + + reader.close(); + reader = null; + } + finally + { + IOUtil.close( reader ); + } + + Writer writer = null; + try + { + interpolatedFile.getParentFile().mkdirs(); + writer = WriterFactory.newXmlWriter( interpolatedFile ); + writer.write( xml ); + writer.close(); + writer = null; + } + finally + { + IOUtil.close( writer ); + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to interpolate file " + originalFile.getPath(), e ); + } + } + + /** + * Gets the (interpolated) invoker properties for an integration test. + * + * @param projectDirectory The base directory of the IT project, must not be <code>null</code>. + * @return The invoker properties, may be empty but never <code>null</code>. + * @throws org.apache.maven.plugin.MojoExecutionException If an I/O error occurred during reading the properties. + */ + private InvokerProperties getInvokerProperties( final File projectDirectory ) + throws MojoExecutionException + { + Properties props = new Properties(); + if ( invokerPropertiesFile != null ) + { + File propertiesFile = new File( projectDirectory, invokerPropertiesFile ); + if ( propertiesFile.isFile() ) + { + InputStream in = null; + try + { + in = new FileInputStream( propertiesFile ); + props.load( in ); + in.close(); + in = null; + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e ); + } + finally + { + IOUtil.close( in ); + } + } + + Interpolator interpolator = new RegexBasedInterpolator(); + interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) ); + // CHECKSTYLE_OFF: LineLength + for ( String key : props.stringPropertyNames() ) + { + String value = props.getProperty( key ); + try + { + value = interpolator.interpolate( value, "" ); + } + catch ( InterpolationException e ) + { + throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile, + e ); + } + props.setProperty( key, value ); + } + // CHECKSTYLE_ON: LineLength + } + return new InvokerProperties( props ); + } + + protected boolean isParallelRun() + { + return parallelThreads > 1; + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/CompositeMap.java b/src/main/java/org/apache/maven/plugins/invoker/CompositeMap.java similarity index 98% rename from src/main/java/org/apache/maven/plugin/invoker/CompositeMap.java rename to src/main/java/org/apache/maven/plugins/invoker/CompositeMap.java index db26ae3..bd1fe37 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/CompositeMap.java +++ b/src/main/java/org/apache/maven/plugins/invoker/CompositeMap.java
@@ -1,259 +1,259 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.introspection.ReflectionValueExtractor; - -/** - * A map-like source to interpolate expressions. - * - * @author Olivier Lamy - * @since 1.1 - * @version $Id$ - */ -class CompositeMap - implements Map<String, Object> -{ - - /** - * The Maven project from which to extract interpolated values, never <code>null</code>. - */ - private MavenProject mavenProject; - - /** - * The set of additional properties from which to extract interpolated values, never <code>null</code>. - */ - private Map<String, Object> properties; - - /** - * Flag indicating to escape XML special characters. - */ - private final boolean escapeXml; - - /** - * Creates a new interpolation source backed by the specified Maven project and some user-specified properties. - * - * @param mavenProject The Maven project from which to extract interpolated values, must not be <code>null</code>. - * @param properties The set of additional properties from which to extract interpolated values, may be - * <code>null</code>. - * @param escapeXml {@code true}, to escape any XML special characters; {@code false}, to not perform any escaping. - */ - protected CompositeMap( MavenProject mavenProject, Map<String, Object> properties, boolean escapeXml ) - { - if ( mavenProject == null ) - { - throw new IllegalArgumentException( "no project specified" ); - } - this.mavenProject = mavenProject; - this.properties = properties == null ? new HashMap<String, Object>() : properties; - this.escapeXml = escapeXml; - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#clear() - */ - public void clear() - { - // nothing here - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#containsKey(java.lang.Object) - */ - public boolean containsKey( Object key ) - { - if ( !( key instanceof String ) ) - { - return false; - } - - String expression = (String) key; - if ( expression.startsWith( "project." ) || expression.startsWith( "pom." ) ) - { - try - { - Object evaluated = ReflectionValueExtractor.evaluate( expression, this.mavenProject ); - if ( evaluated != null ) - { - return true; - } - } - catch ( Exception e ) - { - // uhm do we have to throw a RuntimeException here ? - } - } - - return properties.containsKey( key ) || mavenProject.getProperties().containsKey( key ); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#containsValue(java.lang.Object) - */ - public boolean containsValue( Object value ) - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#entrySet() - */ - public Set<Entry<String, Object>> entrySet() - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#get(java.lang.Object) - */ - public Object get( Object key ) - { - if ( !( key instanceof String ) ) - { - return null; - } - - Object value = null; - String expression = (String) key; - if ( expression.startsWith( "project." ) || expression.startsWith( "pom." ) ) - { - try - { - Object evaluated = ReflectionValueExtractor.evaluate( expression, this.mavenProject ); - if ( evaluated != null ) - { - value = evaluated; - } - } - catch ( Exception e ) - { - // uhm do we have to throw a RuntimeException here ? - } - } - - if ( value == null ) - { - value = properties.get( key ); - } - - if ( value == null ) - { - value = this.mavenProject.getProperties().get( key ); - } - - if ( value != null && this.escapeXml ) - { - value = value.toString(). - replaceAll( "\"", """ ). - replaceAll( "<", "<" ). - replaceAll( ">", ">" ). - replaceAll( "&", "&" ); - - } - - return value; - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#isEmpty() - */ - public boolean isEmpty() - { - return this.mavenProject.getProperties().isEmpty() && this.properties.isEmpty(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#keySet() - */ - public Set<String> keySet() - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#put(java.lang.Object, java.lang.Object) - */ - public Object put( String key, Object value ) - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#putAll(java.util.Map) - */ - public void putAll( Map<? extends String, ? extends Object> t ) - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#remove(java.lang.Object) - */ - public Object remove( Object key ) - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#size() - */ - public int size() - { - throw new UnsupportedOperationException(); - } - - /** - * {@inheritDoc} - * - * @see java.util.Map#values() - */ - public Collection<Object> values() - { - throw new UnsupportedOperationException(); - } -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.introspection.ReflectionValueExtractor; + +/** + * A map-like source to interpolate expressions. + * + * @author Olivier Lamy + * @since 1.1 + * @version $Id: CompositeMap.java 1784076 2017-02-23 00:35:39Z schulte $ + */ +class CompositeMap + implements Map<String, Object> +{ + + /** + * The Maven project from which to extract interpolated values, never <code>null</code>. + */ + private MavenProject mavenProject; + + /** + * The set of additional properties from which to extract interpolated values, never <code>null</code>. + */ + private Map<String, Object> properties; + + /** + * Flag indicating to escape XML special characters. + */ + private final boolean escapeXml; + + /** + * Creates a new interpolation source backed by the specified Maven project and some user-specified properties. + * + * @param mavenProject The Maven project from which to extract interpolated values, must not be <code>null</code>. + * @param properties The set of additional properties from which to extract interpolated values, may be + * <code>null</code>. + * @param escapeXml {@code true}, to escape any XML special characters; {@code false}, to not perform any escaping. + */ + protected CompositeMap( MavenProject mavenProject, Map<String, Object> properties, boolean escapeXml ) + { + if ( mavenProject == null ) + { + throw new IllegalArgumentException( "no project specified" ); + } + this.mavenProject = mavenProject; + this.properties = properties == null ? new HashMap<String, Object>() : properties; + this.escapeXml = escapeXml; + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#clear() + */ + public void clear() + { + // nothing here + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#containsKey(java.lang.Object) + */ + public boolean containsKey( Object key ) + { + if ( !( key instanceof String ) ) + { + return false; + } + + String expression = (String) key; + if ( expression.startsWith( "project." ) || expression.startsWith( "pom." ) ) + { + try + { + Object evaluated = ReflectionValueExtractor.evaluate( expression, this.mavenProject ); + if ( evaluated != null ) + { + return true; + } + } + catch ( Exception e ) + { + // uhm do we have to throw a RuntimeException here ? + } + } + + return properties.containsKey( key ) || mavenProject.getProperties().containsKey( key ); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#containsValue(java.lang.Object) + */ + public boolean containsValue( Object value ) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#entrySet() + */ + public Set<Entry<String, Object>> entrySet() + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#get(java.lang.Object) + */ + public Object get( Object key ) + { + if ( !( key instanceof String ) ) + { + return null; + } + + Object value = null; + String expression = (String) key; + if ( expression.startsWith( "project." ) || expression.startsWith( "pom." ) ) + { + try + { + Object evaluated = ReflectionValueExtractor.evaluate( expression, this.mavenProject ); + if ( evaluated != null ) + { + value = evaluated; + } + } + catch ( Exception e ) + { + // uhm do we have to throw a RuntimeException here ? + } + } + + if ( value == null ) + { + value = properties.get( key ); + } + + if ( value == null ) + { + value = this.mavenProject.getProperties().get( key ); + } + + if ( value != null && this.escapeXml ) + { + value = value.toString(). + replaceAll( "\"", """ ). + replaceAll( "<", "<" ). + replaceAll( ">", ">" ). + replaceAll( "&", "&" ); + + } + + return value; + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#isEmpty() + */ + public boolean isEmpty() + { + return this.mavenProject.getProperties().isEmpty() && this.properties.isEmpty(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#keySet() + */ + public Set<String> keySet() + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + public Object put( String key, Object value ) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#putAll(java.util.Map) + */ + public void putAll( Map<? extends String, ? extends Object> t ) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#remove(java.lang.Object) + */ + public Object remove( Object key ) + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#size() + */ + public int size() + { + throw new UnsupportedOperationException(); + } + + /** + * {@inheritDoc} + * + * @see java.util.Map#values() + */ + public Collection<Object> values() + { + throw new UnsupportedOperationException(); + } +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/FileLogger.java b/src/main/java/org/apache/maven/plugins/invoker/FileLogger.java similarity index 94% rename from src/main/java/org/apache/maven/plugin/invoker/FileLogger.java rename to src/main/java/org/apache/maven/plugins/invoker/FileLogger.java index d8eae3f..1073606 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/FileLogger.java +++ b/src/main/java/org/apache/maven/plugins/invoker/FileLogger.java
@@ -1,62 +1,62 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.IOException; - -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.shared.invoker.InvocationOutputHandler; -import org.apache.maven.shared.scriptinterpreter.ExecutionLogger; - -/** - * @version $Id$ - */ -class FileLogger - extends org.apache.maven.shared.scriptinterpreter.FileLogger - implements InvocationOutputHandler, ExecutionLogger -{ - - /** - * Creates a new logger that writes to the specified file. - * - * @param outputFile The path to the output file, must not be <code>null</code>. - * @throws IOException If the output file could not be created. - */ - public FileLogger( File outputFile ) - throws IOException - { - super( outputFile, null ); - } - - /** - * Creates a new logger that writes to the specified file and optionally mirrors messages to the given mojo logger. - * - * @param outputFile The path to the output file, must not be <code>null</code>. - * @param log The mojo logger to additionally output messages to, may be <code>null</code> if not used. - * @throws IOException If the output file could not be created. - */ - public FileLogger( File outputFile, Log log ) - throws IOException - { - super( outputFile, log ); - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.shared.invoker.InvocationOutputHandler; +import org.apache.maven.shared.scriptinterpreter.ExecutionLogger; + +/** + * @version $Id: FileLogger.java 1671476 2015-04-06 03:53:29Z dantran $ + */ +class FileLogger + extends org.apache.maven.shared.scriptinterpreter.FileLogger + implements InvocationOutputHandler, ExecutionLogger +{ + + /** + * Creates a new logger that writes to the specified file. + * + * @param outputFile The path to the output file, must not be <code>null</code>. + * @throws IOException If the output file could not be created. + */ + public FileLogger( File outputFile ) + throws IOException + { + super( outputFile, null ); + } + + /** + * Creates a new logger that writes to the specified file and optionally mirrors messages to the given mojo logger. + * + * @param outputFile The path to the output file, must not be <code>null</code>. + * @param log The mojo logger to additionally output messages to, may be <code>null</code> if not used. + * @throws IOException If the output file could not be created. + */ + public FileLogger( File outputFile, Log log ) + throws IOException + { + super( outputFile, log ); + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/InstallMojo.java b/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java similarity index 99% rename from src/main/java/org/apache/maven/plugin/invoker/InstallMojo.java rename to src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java index ee23e87..b0a0c21 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/InstallMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InstallMojo.java
@@ -1,605 +1,605 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.IOException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Map; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.artifact.factory.ArtifactFactory; -import org.apache.maven.artifact.repository.ArtifactRepository; -import org.apache.maven.execution.MavenSession; -import org.apache.maven.model.Model; -import org.apache.maven.model.Parent; -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.plugins.annotations.ResolutionScope; -import org.apache.maven.project.MavenProject; -import org.apache.maven.project.ProjectBuildingRequest; -import org.apache.maven.shared.artifact.install.ArtifactInstaller; -import org.apache.maven.shared.dependencies.DefaultDependableCoordinate; -import org.apache.maven.shared.dependencies.resolve.DependencyResolver; -import org.apache.maven.shared.dependencies.resolve.DependencyResolverException; -import org.apache.maven.shared.repository.RepositoryManager; -import org.codehaus.plexus.util.FileUtils; - -/** - * Installs the project artifacts of the main build into the local repository as a preparation to run the sub projects. - * More precisely, all artifacts of the project itself, all its locally reachable parent POMs and all its dependencies - * from the reactor will be installed to the local repository. - * - * @since 1.2 - * @author Paul Gier - * @author Benjamin Bentmann - * @version $Id$ - */ -// CHECKSTYLE_OFF: LineLength -@Mojo( name = "install", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true ) -// CHECKSTYLE_ON: LineLength -public class InstallMojo - extends AbstractMojo -{ - - /** - * Maven artifact install component to copy artifacts to the local repository. - */ - @Component - private ArtifactInstaller installer; - - @Component - private RepositoryManager repositoryManager; - - /** - * The component used to create artifacts. - */ - @Component - private ArtifactFactory artifactFactory; - - /** - */ - @Parameter( property = "localRepository", required = true, readonly = true ) - private ArtifactRepository localRepository; - - /** - * The path to the local repository into which the project artifacts should be installed for the integration tests. - * If not set, the regular local repository will be used. To prevent soiling of your regular local repository with - * possibly broken artifacts, it is strongly recommended to use an isolated repository for the integration tests - * (e.g. <code>${project.build.directory}/it-repo</code>). - */ - @Parameter( property = "invoker.localRepositoryPath", - defaultValue = "${session.localRepository.basedir}", required = true ) - private File localRepositoryPath; - - /** - * The current Maven project. - */ - @Parameter( defaultValue = "${project}", readonly = true, required = true ) - private MavenProject project; - - @Parameter( defaultValue = "${session}", readonly = true, required = true ) - private MavenSession session; - - /** - * The set of Maven projects in the reactor build. - */ - @Parameter( defaultValue = "${reactorProjects}", readonly = true ) - private Collection<MavenProject> reactorProjects; - - /** - * A flag used to disable the installation procedure. This is primarily intended for usage from the command line to - * occasionally adjust the build. - * - * @since 1.4 - */ - @Parameter( property = "invoker.skip", defaultValue = "false" ) - private boolean skipInstallation; - - /** - * The identifiers of already installed artifacts, used to avoid multiple installation of the same artifact. - */ - private Collection<String> installedArtifacts; - - /** - * The identifiers of already copied artifacts, used to avoid multiple installation of the same artifact. - */ - private Collection<String> copiedArtifacts; - - /** - * Extra dependencies that need to be installed on the local repository.<BR> - * Format: - * - * <pre> - * groupId:artifactId:version:type:classifier - * </pre> - * - * Examples: - * - * <pre> - * org.apache.maven.plugins:maven-clean-plugin:2.4:maven-plugin - * org.apache.maven.plugins:maven-clean-plugin:2.4:jar:javadoc - * </pre> - * - * If the type is 'maven-plugin' the plugin will try to resolve the artifact using plugin remote repositories, - * instead of using artifact remote repositories. - * - * @since 1.6 - */ - @Parameter - private String[] extraArtifacts; - - /** - */ - @Component - private DependencyResolver resolver; - - private ProjectBuildingRequest projectBuildingRequest; - - /** - * Performs this mojo's tasks. - * - * @throws MojoExecutionException If the artifacts could not be installed. - */ - public void execute() - throws MojoExecutionException - { - if ( skipInstallation ) - { - getLog().info( "Skipping artifact installation per configuration." ); - return; - } - - createTestRepository(); - - installedArtifacts = new HashSet<String>(); - copiedArtifacts = new HashSet<String>(); - - installProjectDependencies( project, reactorProjects ); - installProjectParents( project ); - installProjectArtifacts( project ); - - installExtraArtifacts( extraArtifacts ); - } - - /** - * Creates the local repository for the integration tests. If the user specified a custom repository location, the - * custom repository will have the same identifier, layout and policies as the real local repository. That means - * apart from the location, the custom repository will be indistinguishable from the real repository such that its - * usage is transparent to the integration tests. - * - * @return The local repository for the integration tests, never <code>null</code>. - * @throws MojoExecutionException If the repository could not be created. - */ - private void createTestRepository() - throws MojoExecutionException - { - - if ( !localRepositoryPath.exists() && !localRepositoryPath.mkdirs() ) - { - throw new MojoExecutionException( "Failed to create directory: " + localRepositoryPath ); - } - projectBuildingRequest = - repositoryManager.setLocalRepositoryBasedir( session.getProjectBuildingRequest(), localRepositoryPath ); - } - - /** - * Installs the specified artifact to the local repository. Note: This method should only be used for artifacts that - * originate from the current (reactor) build. Artifacts that have been grabbed from the user's local repository - * should be installed to the test repository via {@link #copyArtifact(File, Artifact)}. - * - * @param file The file associated with the artifact, must not be <code>null</code>. This is in most cases the value - * of <code>artifact.getFile()</code> with the exception of the main artifact from a project with - * packaging "pom". Projects with packaging "pom" have no main artifact file. They have however artifact - * metadata (e.g. site descriptors) which needs to be installed. - * @param artifact The artifact to install, must not be <code>null</code>. - * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). - */ - private void installArtifact( File file, Artifact artifact ) - throws MojoExecutionException - { - try - { - if ( file == null ) - { - throw new IllegalStateException( "Artifact has no associated file: " + artifact.getId() ); - } - if ( !file.isFile() ) - { - throw new IllegalStateException( "Artifact is not fully assembled: " + file ); - } - - if ( installedArtifacts.add( artifact.getId() ) ) - { - artifact.setFile( file ); - installer.install( projectBuildingRequest, localRepositoryPath, - Collections.singletonList( artifact ) ); - } - else - { - getLog().debug( "Not re-installing " + artifact + ", " + file ); - } - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to install artifact: " + artifact, e ); - } - } - - /** - * Installs the specified artifact to the local repository. This method serves basically the same purpose as - * {@link #installArtifact(File, Artifact)} but is meant for artifacts that have been resolved - * from the user's local repository (and not the current build outputs). The subtle difference here is that - * artifacts from the repository have already undergone transformations and these manipulations should not be redone - * by the artifact installer. For this reason, this method performs plain copy operations to install the artifacts. - * - * @param file The file associated with the artifact, must not be <code>null</code>. - * @param artifact The artifact to install, must not be <code>null</code>. - * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). - */ - private void copyArtifact( File file, Artifact artifact ) - throws MojoExecutionException - { - try - { - if ( file == null ) - { - throw new IllegalStateException( "Artifact has no associated file: " + artifact.getId() ); - } - if ( !file.isFile() ) - { - throw new IllegalStateException( "Artifact is not fully assembled: " + file ); - } - - if ( copiedArtifacts.add( artifact.getId() ) ) - { - File destination = - new File( localRepositoryPath, - repositoryManager.getPathForLocalArtifact( projectBuildingRequest, artifact ) ); - - getLog().debug( "Installing " + file + " to " + destination ); - - copyFileIfDifferent( file, destination ); - - MetadataUtils.createMetadata( destination, artifact ); - } - else - { - getLog().debug( "Not re-installing " + artifact + ", " + file ); - } - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to stage artifact: " + artifact, e ); - } - } - - private void copyFileIfDifferent( File src, File dst ) - throws IOException - { - if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() ) - { - FileUtils.copyFile( src, dst ); - dst.setLastModified( src.lastModified() ); - } - } - - /** - * Installs the main artifact and any attached artifacts of the specified project to the local repository. - * - * @param mvnProject The project whose artifacts should be installed, must not be <code>null</code>. - * @throws MojoExecutionException If any artifact could not be installed. - */ - private void installProjectArtifacts( MavenProject mvnProject ) - throws MojoExecutionException - { - try - { - // Install POM (usually attached as metadata but that happens only as a side effect of the Install Plugin) - installProjectPom( mvnProject ); - - // Install the main project artifact (if the project has one, e.g. has no "pom" packaging) - Artifact mainArtifact = mvnProject.getArtifact(); - if ( mainArtifact.getFile() != null ) - { - installArtifact( mainArtifact.getFile(), mainArtifact ); - } - - // Install any attached project artifacts - Collection<Artifact> attachedArtifacts = (Collection<Artifact>) mvnProject.getAttachedArtifacts(); - for ( Artifact attachedArtifact : attachedArtifacts ) - { - installArtifact( attachedArtifact.getFile(), attachedArtifact ); - } - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to install project artifacts: " + mvnProject, e ); - } - } - - /** - * Installs the (locally reachable) parent POMs of the specified project to the local repository. The parent POMs - * from the reactor must be installed or the forked IT builds will fail when using a clean repository. - * - * @param mvnProject The project whose parent POMs should be installed, must not be <code>null</code>. - * @throws MojoExecutionException If any POM could not be installed. - */ - private void installProjectParents( MavenProject mvnProject ) - throws MojoExecutionException - { - try - { - for ( MavenProject parent = mvnProject.getParent(); parent != null; parent = parent.getParent() ) - { - if ( parent.getFile() == null ) - { - copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); - break; - } - installProjectPom( parent ); - } - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to install project parents: " + mvnProject, e ); - } - } - - /** - * Installs the POM of the specified project to the local repository. - * - * @param mvnProject The project whose POM should be installed, must not be <code>null</code>. - * @throws MojoExecutionException If the POM could not be installed. - */ - private void installProjectPom( MavenProject mvnProject ) - throws MojoExecutionException - { - try - { - Artifact pomArtifact = null; - if ( "pom".equals( mvnProject.getPackaging() ) ) - { - pomArtifact = mvnProject.getArtifact(); - } - if ( pomArtifact == null ) - { - pomArtifact = - artifactFactory.createProjectArtifact( mvnProject.getGroupId(), mvnProject.getArtifactId(), - mvnProject.getVersion() ); - } - installArtifact( mvnProject.getFile(), pomArtifact ); - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to install POM: " + mvnProject, e ); - } - } - - /** - * Installs the dependent projects from the reactor to the local repository. The dependencies on other modules from - * the reactor must be installed or the forked IT builds will fail when using a clean repository. - * - * @param mvnProject The project whose dependent projects should be installed, must not be <code>null</code>. - * @param reactorProjects The set of projects in the reactor build, must not be <code>null</code>. - * @throws MojoExecutionException If any dependency could not be installed. - */ - private void installProjectDependencies( MavenProject mvnProject, Collection<MavenProject> reactorProjects ) - throws MojoExecutionException - { - // keep track if we have passed mvnProject in reactorProjects - boolean foundCurrent = false; - - // ... into dependencies that were resolved from reactor projects ... - Collection<String> dependencyProjects = new LinkedHashSet<String>(); - - // index available reactor projects - Map<String, MavenProject> projects = new HashMap<String, MavenProject>( reactorProjects.size() ); - for ( MavenProject reactorProject : reactorProjects ) - { - String projectId = - reactorProject.getGroupId() + ':' + reactorProject.getArtifactId() + ':' + reactorProject.getVersion(); - - projects.put( projectId, reactorProject ); - - // only add projects of reactor build previous to this mvnProject - foundCurrent |= ( mvnProject.equals( reactorProject ) ); - if ( !foundCurrent ) - { - dependencyProjects.add( projectId ); - } - } - - // group transitive dependencies (even those that don't contribute to the class path like POMs) ... - Collection<Artifact> artifacts = (Collection<Artifact>) mvnProject.getArtifacts(); - // ... and those that were resolved from the (local) repo - Collection<Artifact> dependencyArtifacts = new LinkedHashSet<Artifact>(); - - for ( Artifact artifact : artifacts ) - { - // workaround for MNG-2961 to ensure the base version does not contain a timestamp - artifact.isSnapshot(); - - String projectId = artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); - - if ( !projects.containsKey( projectId ) ) - { - dependencyArtifacts.add( artifact ); - } - } - - // install dependencies - try - { - // copy dependencies that where resolved from the local repo - for ( Artifact artifact : dependencyArtifacts ) - { - copyArtifact( artifact ); - } - - // install dependencies that were resolved from the reactor - for ( String projectId : dependencyProjects ) - { - MavenProject dependencyProject = projects.get( projectId ); - - installProjectArtifacts( dependencyProject ); - installProjectParents( dependencyProject ); - } - } - catch ( Exception e ) - { - throw new MojoExecutionException( "Failed to install project dependencies: " + mvnProject, e ); - } - } - - private void copyArtifact( Artifact artifact ) - throws MojoExecutionException - { - copyPoms( artifact ); - - Artifact depArtifact = - artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(), - artifact.getBaseVersion(), artifact.getType(), - artifact.getClassifier() ); - - File artifactFile = artifact.getFile(); - - copyArtifact( artifactFile, depArtifact ); - } - - private void copyPoms( Artifact artifact ) - throws MojoExecutionException - { - Artifact pomArtifact = - artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), - artifact.getBaseVersion() ); - - File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); - - if ( pomFile.isFile() ) - { - copyArtifact( pomFile, pomArtifact ); - copyParentPoms( pomFile ); - } - } - - /** - * Installs all parent POMs of the specified POM file that are available in the local repository. - * - * @param pomFile The path to the POM file whose parents should be installed, must not be <code>null</code>. - * @throws MojoExecutionException If any (existing) parent POM could not be installed. - */ - private void copyParentPoms( File pomFile ) - throws MojoExecutionException - { - Model model = PomUtils.loadPom( pomFile ); - Parent parent = model.getParent(); - if ( parent != null ) - { - copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); - } - } - - /** - * Installs the specified POM and all its parent POMs to the local repository. - * - * @param groupId The group id of the POM which should be installed, must not be <code>null</code>. - * @param artifactId The artifact id of the POM which should be installed, must not be <code>null</code>. - * @param version The version of the POM which should be installed, must not be <code>null</code>. - * @throws MojoExecutionException If any (existing) parent POM could not be installed. - */ - private void copyParentPoms( String groupId, String artifactId, String version ) - throws MojoExecutionException - { - Artifact pomArtifact = artifactFactory.createProjectArtifact( groupId, artifactId, version ); - - if ( installedArtifacts.contains( pomArtifact.getId() ) || copiedArtifacts.contains( pomArtifact.getId() ) ) - { - getLog().debug( "Not re-installing " + pomArtifact ); - return; - } - - File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); - if ( pomFile.isFile() ) - { - copyArtifact( pomFile, pomArtifact ); - copyParentPoms( pomFile ); - } - } - - private void installExtraArtifacts( String[] extraArtifacts ) - throws MojoExecutionException - { - if ( extraArtifacts == null ) - { - return; - } - - for ( String extraArtifact : extraArtifacts ) - { - String[] gav = extraArtifact.split( ":" ); - if ( gav.length < 3 || gav.length > 5 ) - { - throw new MojoExecutionException( "Invalid artifact " + extraArtifact ); - } - - String groupId = gav[0]; - String artifactId = gav[1]; - String version = gav[2]; - - String type = "jar"; - if ( gav.length > 3 ) - { - type = gav[3]; - } - - String classifier = null; - if ( gav.length == 5 ) - { - classifier = gav[4]; - } - - DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate(); - try - { - coordinate.setGroupId( groupId ); - coordinate.setArtifactId( artifactId ); - coordinate.setVersion( version ); - coordinate.setType( type ); - coordinate.setClassifier( classifier ); - - resolver.resolveDependencies( projectBuildingRequest, coordinate, null ); - } - catch ( DependencyResolverException e ) - { - throw new MojoExecutionException( "Unable to resolve dependencies for: " + coordinate, e ); - } - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.factory.ArtifactFactory; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.model.Model; +import org.apache.maven.model.Parent; +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.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.shared.artifact.install.ArtifactInstaller; +import org.apache.maven.shared.dependencies.DefaultDependableCoordinate; +import org.apache.maven.shared.dependencies.resolve.DependencyResolver; +import org.apache.maven.shared.dependencies.resolve.DependencyResolverException; +import org.apache.maven.shared.repository.RepositoryManager; +import org.codehaus.plexus.util.FileUtils; + +/** + * Installs the project artifacts of the main build into the local repository as a preparation to run the sub projects. + * More precisely, all artifacts of the project itself, all its locally reachable parent POMs and all its dependencies + * from the reactor will be installed to the local repository. + * + * @since 1.2 + * @author Paul Gier + * @author Benjamin Bentmann + * @version $Id: InstallMojo.java 1748000 2016-06-12 13:20:59Z rfscholte $ + */ +// CHECKSTYLE_OFF: LineLength +@Mojo( name = "install", defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.RUNTIME, threadSafe = true ) +// CHECKSTYLE_ON: LineLength +public class InstallMojo + extends AbstractMojo +{ + + /** + * Maven artifact install component to copy artifacts to the local repository. + */ + @Component + private ArtifactInstaller installer; + + @Component + private RepositoryManager repositoryManager; + + /** + * The component used to create artifacts. + */ + @Component + private ArtifactFactory artifactFactory; + + /** + */ + @Parameter( property = "localRepository", required = true, readonly = true ) + private ArtifactRepository localRepository; + + /** + * The path to the local repository into which the project artifacts should be installed for the integration tests. + * If not set, the regular local repository will be used. To prevent soiling of your regular local repository with + * possibly broken artifacts, it is strongly recommended to use an isolated repository for the integration tests + * (e.g. <code>${project.build.directory}/it-repo</code>). + */ + @Parameter( property = "invoker.localRepositoryPath", + defaultValue = "${session.localRepository.basedir}", required = true ) + private File localRepositoryPath; + + /** + * The current Maven project. + */ + @Parameter( defaultValue = "${project}", readonly = true, required = true ) + private MavenProject project; + + @Parameter( defaultValue = "${session}", readonly = true, required = true ) + private MavenSession session; + + /** + * The set of Maven projects in the reactor build. + */ + @Parameter( defaultValue = "${reactorProjects}", readonly = true ) + private Collection<MavenProject> reactorProjects; + + /** + * A flag used to disable the installation procedure. This is primarily intended for usage from the command line to + * occasionally adjust the build. + * + * @since 1.4 + */ + @Parameter( property = "invoker.skip", defaultValue = "false" ) + private boolean skipInstallation; + + /** + * The identifiers of already installed artifacts, used to avoid multiple installation of the same artifact. + */ + private Collection<String> installedArtifacts; + + /** + * The identifiers of already copied artifacts, used to avoid multiple installation of the same artifact. + */ + private Collection<String> copiedArtifacts; + + /** + * Extra dependencies that need to be installed on the local repository.<BR> + * Format: + * + * <pre> + * groupId:artifactId:version:type:classifier + * </pre> + * + * Examples: + * + * <pre> + * org.apache.maven.plugins:maven-clean-plugin:2.4:maven-plugin + * org.apache.maven.plugins:maven-clean-plugin:2.4:jar:javadoc + * </pre> + * + * If the type is 'maven-plugin' the plugin will try to resolve the artifact using plugin remote repositories, + * instead of using artifact remote repositories. + * + * @since 1.6 + */ + @Parameter + private String[] extraArtifacts; + + /** + */ + @Component + private DependencyResolver resolver; + + private ProjectBuildingRequest projectBuildingRequest; + + /** + * Performs this mojo's tasks. + * + * @throws MojoExecutionException If the artifacts could not be installed. + */ + public void execute() + throws MojoExecutionException + { + if ( skipInstallation ) + { + getLog().info( "Skipping artifact installation per configuration." ); + return; + } + + createTestRepository(); + + installedArtifacts = new HashSet<String>(); + copiedArtifacts = new HashSet<String>(); + + installProjectDependencies( project, reactorProjects ); + installProjectParents( project ); + installProjectArtifacts( project ); + + installExtraArtifacts( extraArtifacts ); + } + + /** + * Creates the local repository for the integration tests. If the user specified a custom repository location, the + * custom repository will have the same identifier, layout and policies as the real local repository. That means + * apart from the location, the custom repository will be indistinguishable from the real repository such that its + * usage is transparent to the integration tests. + * + * @return The local repository for the integration tests, never <code>null</code>. + * @throws MojoExecutionException If the repository could not be created. + */ + private void createTestRepository() + throws MojoExecutionException + { + + if ( !localRepositoryPath.exists() && !localRepositoryPath.mkdirs() ) + { + throw new MojoExecutionException( "Failed to create directory: " + localRepositoryPath ); + } + projectBuildingRequest = + repositoryManager.setLocalRepositoryBasedir( session.getProjectBuildingRequest(), localRepositoryPath ); + } + + /** + * Installs the specified artifact to the local repository. Note: This method should only be used for artifacts that + * originate from the current (reactor) build. Artifacts that have been grabbed from the user's local repository + * should be installed to the test repository via {@link #copyArtifact(File, Artifact)}. + * + * @param file The file associated with the artifact, must not be <code>null</code>. This is in most cases the value + * of <code>artifact.getFile()</code> with the exception of the main artifact from a project with + * packaging "pom". Projects with packaging "pom" have no main artifact file. They have however artifact + * metadata (e.g. site descriptors) which needs to be installed. + * @param artifact The artifact to install, must not be <code>null</code>. + * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). + */ + private void installArtifact( File file, Artifact artifact ) + throws MojoExecutionException + { + try + { + if ( file == null ) + { + throw new IllegalStateException( "Artifact has no associated file: " + artifact.getId() ); + } + if ( !file.isFile() ) + { + throw new IllegalStateException( "Artifact is not fully assembled: " + file ); + } + + if ( installedArtifacts.add( artifact.getId() ) ) + { + artifact.setFile( file ); + installer.install( projectBuildingRequest, localRepositoryPath, + Collections.singletonList( artifact ) ); + } + else + { + getLog().debug( "Not re-installing " + artifact + ", " + file ); + } + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to install artifact: " + artifact, e ); + } + } + + /** + * Installs the specified artifact to the local repository. This method serves basically the same purpose as + * {@link #installArtifact(File, Artifact)} but is meant for artifacts that have been resolved + * from the user's local repository (and not the current build outputs). The subtle difference here is that + * artifacts from the repository have already undergone transformations and these manipulations should not be redone + * by the artifact installer. For this reason, this method performs plain copy operations to install the artifacts. + * + * @param file The file associated with the artifact, must not be <code>null</code>. + * @param artifact The artifact to install, must not be <code>null</code>. + * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). + */ + private void copyArtifact( File file, Artifact artifact ) + throws MojoExecutionException + { + try + { + if ( file == null ) + { + throw new IllegalStateException( "Artifact has no associated file: " + artifact.getId() ); + } + if ( !file.isFile() ) + { + throw new IllegalStateException( "Artifact is not fully assembled: " + file ); + } + + if ( copiedArtifacts.add( artifact.getId() ) ) + { + File destination = + new File( localRepositoryPath, + repositoryManager.getPathForLocalArtifact( projectBuildingRequest, artifact ) ); + + getLog().debug( "Installing " + file + " to " + destination ); + + copyFileIfDifferent( file, destination ); + + MetadataUtils.createMetadata( destination, artifact ); + } + else + { + getLog().debug( "Not re-installing " + artifact + ", " + file ); + } + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to stage artifact: " + artifact, e ); + } + } + + private void copyFileIfDifferent( File src, File dst ) + throws IOException + { + if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() ) + { + FileUtils.copyFile( src, dst ); + dst.setLastModified( src.lastModified() ); + } + } + + /** + * Installs the main artifact and any attached artifacts of the specified project to the local repository. + * + * @param mvnProject The project whose artifacts should be installed, must not be <code>null</code>. + * @throws MojoExecutionException If any artifact could not be installed. + */ + private void installProjectArtifacts( MavenProject mvnProject ) + throws MojoExecutionException + { + try + { + // Install POM (usually attached as metadata but that happens only as a side effect of the Install Plugin) + installProjectPom( mvnProject ); + + // Install the main project artifact (if the project has one, e.g. has no "pom" packaging) + Artifact mainArtifact = mvnProject.getArtifact(); + if ( mainArtifact.getFile() != null ) + { + installArtifact( mainArtifact.getFile(), mainArtifact ); + } + + // Install any attached project artifacts + Collection<Artifact> attachedArtifacts = (Collection<Artifact>) mvnProject.getAttachedArtifacts(); + for ( Artifact attachedArtifact : attachedArtifacts ) + { + installArtifact( attachedArtifact.getFile(), attachedArtifact ); + } + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to install project artifacts: " + mvnProject, e ); + } + } + + /** + * Installs the (locally reachable) parent POMs of the specified project to the local repository. The parent POMs + * from the reactor must be installed or the forked IT builds will fail when using a clean repository. + * + * @param mvnProject The project whose parent POMs should be installed, must not be <code>null</code>. + * @throws MojoExecutionException If any POM could not be installed. + */ + private void installProjectParents( MavenProject mvnProject ) + throws MojoExecutionException + { + try + { + for ( MavenProject parent = mvnProject.getParent(); parent != null; parent = parent.getParent() ) + { + if ( parent.getFile() == null ) + { + copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); + break; + } + installProjectPom( parent ); + } + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to install project parents: " + mvnProject, e ); + } + } + + /** + * Installs the POM of the specified project to the local repository. + * + * @param mvnProject The project whose POM should be installed, must not be <code>null</code>. + * @throws MojoExecutionException If the POM could not be installed. + */ + private void installProjectPom( MavenProject mvnProject ) + throws MojoExecutionException + { + try + { + Artifact pomArtifact = null; + if ( "pom".equals( mvnProject.getPackaging() ) ) + { + pomArtifact = mvnProject.getArtifact(); + } + if ( pomArtifact == null ) + { + pomArtifact = + artifactFactory.createProjectArtifact( mvnProject.getGroupId(), mvnProject.getArtifactId(), + mvnProject.getVersion() ); + } + installArtifact( mvnProject.getFile(), pomArtifact ); + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to install POM: " + mvnProject, e ); + } + } + + /** + * Installs the dependent projects from the reactor to the local repository. The dependencies on other modules from + * the reactor must be installed or the forked IT builds will fail when using a clean repository. + * + * @param mvnProject The project whose dependent projects should be installed, must not be <code>null</code>. + * @param reactorProjects The set of projects in the reactor build, must not be <code>null</code>. + * @throws MojoExecutionException If any dependency could not be installed. + */ + private void installProjectDependencies( MavenProject mvnProject, Collection<MavenProject> reactorProjects ) + throws MojoExecutionException + { + // keep track if we have passed mvnProject in reactorProjects + boolean foundCurrent = false; + + // ... into dependencies that were resolved from reactor projects ... + Collection<String> dependencyProjects = new LinkedHashSet<String>(); + + // index available reactor projects + Map<String, MavenProject> projects = new HashMap<String, MavenProject>( reactorProjects.size() ); + for ( MavenProject reactorProject : reactorProjects ) + { + String projectId = + reactorProject.getGroupId() + ':' + reactorProject.getArtifactId() + ':' + reactorProject.getVersion(); + + projects.put( projectId, reactorProject ); + + // only add projects of reactor build previous to this mvnProject + foundCurrent |= ( mvnProject.equals( reactorProject ) ); + if ( !foundCurrent ) + { + dependencyProjects.add( projectId ); + } + } + + // group transitive dependencies (even those that don't contribute to the class path like POMs) ... + Collection<Artifact> artifacts = (Collection<Artifact>) mvnProject.getArtifacts(); + // ... and those that were resolved from the (local) repo + Collection<Artifact> dependencyArtifacts = new LinkedHashSet<Artifact>(); + + for ( Artifact artifact : artifacts ) + { + // workaround for MNG-2961 to ensure the base version does not contain a timestamp + artifact.isSnapshot(); + + String projectId = artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); + + if ( !projects.containsKey( projectId ) ) + { + dependencyArtifacts.add( artifact ); + } + } + + // install dependencies + try + { + // copy dependencies that where resolved from the local repo + for ( Artifact artifact : dependencyArtifacts ) + { + copyArtifact( artifact ); + } + + // install dependencies that were resolved from the reactor + for ( String projectId : dependencyProjects ) + { + MavenProject dependencyProject = projects.get( projectId ); + + installProjectArtifacts( dependencyProject ); + installProjectParents( dependencyProject ); + } + } + catch ( Exception e ) + { + throw new MojoExecutionException( "Failed to install project dependencies: " + mvnProject, e ); + } + } + + private void copyArtifact( Artifact artifact ) + throws MojoExecutionException + { + copyPoms( artifact ); + + Artifact depArtifact = + artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(), + artifact.getBaseVersion(), artifact.getType(), + artifact.getClassifier() ); + + File artifactFile = artifact.getFile(); + + copyArtifact( artifactFile, depArtifact ); + } + + private void copyPoms( Artifact artifact ) + throws MojoExecutionException + { + Artifact pomArtifact = + artifactFactory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), + artifact.getBaseVersion() ); + + File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); + + if ( pomFile.isFile() ) + { + copyArtifact( pomFile, pomArtifact ); + copyParentPoms( pomFile ); + } + } + + /** + * Installs all parent POMs of the specified POM file that are available in the local repository. + * + * @param pomFile The path to the POM file whose parents should be installed, must not be <code>null</code>. + * @throws MojoExecutionException If any (existing) parent POM could not be installed. + */ + private void copyParentPoms( File pomFile ) + throws MojoExecutionException + { + Model model = PomUtils.loadPom( pomFile ); + Parent parent = model.getParent(); + if ( parent != null ) + { + copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion() ); + } + } + + /** + * Installs the specified POM and all its parent POMs to the local repository. + * + * @param groupId The group id of the POM which should be installed, must not be <code>null</code>. + * @param artifactId The artifact id of the POM which should be installed, must not be <code>null</code>. + * @param version The version of the POM which should be installed, must not be <code>null</code>. + * @throws MojoExecutionException If any (existing) parent POM could not be installed. + */ + private void copyParentPoms( String groupId, String artifactId, String version ) + throws MojoExecutionException + { + Artifact pomArtifact = artifactFactory.createProjectArtifact( groupId, artifactId, version ); + + if ( installedArtifacts.contains( pomArtifact.getId() ) || copiedArtifacts.contains( pomArtifact.getId() ) ) + { + getLog().debug( "Not re-installing " + pomArtifact ); + return; + } + + File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); + if ( pomFile.isFile() ) + { + copyArtifact( pomFile, pomArtifact ); + copyParentPoms( pomFile ); + } + } + + private void installExtraArtifacts( String[] extraArtifacts ) + throws MojoExecutionException + { + if ( extraArtifacts == null ) + { + return; + } + + for ( String extraArtifact : extraArtifacts ) + { + String[] gav = extraArtifact.split( ":" ); + if ( gav.length < 3 || gav.length > 5 ) + { + throw new MojoExecutionException( "Invalid artifact " + extraArtifact ); + } + + String groupId = gav[0]; + String artifactId = gav[1]; + String version = gav[2]; + + String type = "jar"; + if ( gav.length > 3 ) + { + type = gav[3]; + } + + String classifier = null; + if ( gav.length == 5 ) + { + classifier = gav[4]; + } + + DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate(); + try + { + coordinate.setGroupId( groupId ); + coordinate.setArtifactId( artifactId ); + coordinate.setVersion( version ); + coordinate.setType( type ); + coordinate.setClassifier( classifier ); + + resolver.resolveDependencies( projectBuildingRequest, coordinate, null ); + } + catch ( DependencyResolverException e ) + { + throw new MojoExecutionException( "Unable to resolve dependencies for: " + coordinate, e ); + } + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/IntegrationTestMojo.java b/src/main/java/org/apache/maven/plugins/invoker/IntegrationTestMojo.java similarity index 93% rename from src/main/java/org/apache/maven/plugin/invoker/IntegrationTestMojo.java rename to src/main/java/org/apache/maven/plugins/invoker/IntegrationTestMojo.java index 4f8eb72..07d76a6 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/IntegrationTestMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/IntegrationTestMojo.java
@@ -1,48 +1,48 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.ResolutionScope; - -/** - * Searches for integration test Maven projects, and executes each, collecting a log in the project directory, will - * never fail the build, designed to be used in conjunction with the verify mojo. - * - * @since 1.4 - * @author <a href="mailto:stephenconnolly at codehaus">Stephen Connolly</a> - * @version $Id$ - */ -// CHECKSTYLE_OFF: LineLength -@Mojo( name = "integration-test", defaultPhase = LifecyclePhase.INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) -public class IntegrationTestMojo - extends AbstractInvokerMojo -{ - - void processResults( InvokerSession invokerSession ) - throws MojoFailureException - { - // do nothing - } - -} -// CHECKSTYLE_ON: LineLength +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.ResolutionScope; + +/** + * Searches for integration test Maven projects, and executes each, collecting a log in the project directory, will + * never fail the build, designed to be used in conjunction with the verify mojo. + * + * @since 1.4 + * @author <a href="mailto:stephenconnolly at codehaus">Stephen Connolly</a> + * @version $Id: IntegrationTestMojo.java 1637968 2014-11-10 20:02:25Z khmarbaise $ + */ +// CHECKSTYLE_OFF: LineLength +@Mojo( name = "integration-test", defaultPhase = LifecyclePhase.INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) +public class IntegrationTestMojo + extends AbstractInvokerMojo +{ + + void processResults( InvokerSession invokerSession ) + throws MojoFailureException + { + // do nothing + } + +} +// CHECKSTYLE_ON: LineLength
diff --git a/src/main/java/org/apache/maven/plugin/invoker/InvokerMojo.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerMojo.java similarity index 95% rename from src/main/java/org/apache/maven/plugin/invoker/InvokerMojo.java rename to src/main/java/org/apache/maven/plugins/invoker/InvokerMojo.java index a4d7813..2b36291 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/InvokerMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerMojo.java
@@ -1,82 +1,82 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import org.apache.maven.plugin.MojoFailureException; -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; - -/** - * Searches for integration test Maven projects, and executes each, collecting a log in the project directory, and - * outputting the results to the command line. - * - * @since 1.0 - * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a> - * @author <a href="mailto:jdcasey@apache.org">John Casey</a> - * @version $Id$ - */ -// CHECKSTYLE_OFF: LineLength -@Mojo( name = "run", defaultPhase = LifecyclePhase.INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) -// CHECKSTYLE_ON: LineLength -public class InvokerMojo - extends AbstractInvokerMojo -{ - - /** - * A flag controlling whether failures of the sub builds should fail the main build, too. If set to - * <code>true</code>, the main build will proceed even if one or more sub builds failed. - * - * @since 1.3 - */ - @Parameter( property = "maven.test.failure.ignore", defaultValue = "false" ) - private boolean ignoreFailures; - - /** - * Set this to <code>true</code> to cause a failure if there are no projects to invoke. - * - * @since 1.9 - */ - @Parameter( property = "invoker.failIfNoProjects" ) - private Boolean failIfNoProjects; - - void processResults( InvokerSession invokerSession ) - throws MojoFailureException - { - if ( !suppressSummaries ) - { - invokerSession.logSummary( getLog(), ignoreFailures ); - } - - invokerSession.handleFailures( getLog(), ignoreFailures ); - } - - @Override - protected void doFailIfNoProjects() - throws MojoFailureException - { - if ( Boolean.TRUE.equals( failIfNoProjects ) ) - { - throw new MojoFailureException( "No projects to invoke!" ); - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import org.apache.maven.plugin.MojoFailureException; +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; + +/** + * Searches for integration test Maven projects, and executes each, collecting a log in the project directory, and + * outputting the results to the command line. + * + * @since 1.0 + * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a> + * @author <a href="mailto:jdcasey@apache.org">John Casey</a> + * @version $Id: InvokerMojo.java 1637968 2014-11-10 20:02:25Z khmarbaise $ + */ +// CHECKSTYLE_OFF: LineLength +@Mojo( name = "run", defaultPhase = LifecyclePhase.INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) +// CHECKSTYLE_ON: LineLength +public class InvokerMojo + extends AbstractInvokerMojo +{ + + /** + * A flag controlling whether failures of the sub builds should fail the main build, too. If set to + * <code>true</code>, the main build will proceed even if one or more sub builds failed. + * + * @since 1.3 + */ + @Parameter( property = "maven.test.failure.ignore", defaultValue = "false" ) + private boolean ignoreFailures; + + /** + * Set this to <code>true</code> to cause a failure if there are no projects to invoke. + * + * @since 1.9 + */ + @Parameter( property = "invoker.failIfNoProjects" ) + private Boolean failIfNoProjects; + + void processResults( InvokerSession invokerSession ) + throws MojoFailureException + { + if ( !suppressSummaries ) + { + invokerSession.logSummary( getLog(), ignoreFailures ); + } + + invokerSession.handleFailures( getLog(), ignoreFailures ); + } + + @Override + protected void doFailIfNoProjects() + throws MojoFailureException + { + if ( Boolean.TRUE.equals( failIfNoProjects ) ) + { + throw new MojoFailureException( "No projects to invoke!" ); + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/InvokerProperties.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java similarity index 98% rename from src/main/java/org/apache/maven/plugin/invoker/InvokerProperties.java rename to src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java index eecab9a..97a40ee 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/InvokerProperties.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerProperties.java
@@ -1,365 +1,365 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Properties; - -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.InvocationRequest.ReactorFailureBehavior; -import org.codehaus.plexus.util.StringUtils; - -/** - * Provides a convenient facade around the <code>invoker.properties</code>. - * - * @author Benjamin Bentmann - * @version $Id$ - */ -class InvokerProperties -{ - private static final String SELECTOR_PREFIX = "selector."; - - private enum InvocationProperty - { - PROJECT( "invoker.project" ), - GOALS( "invoker.goals" ), - PROFILES( "invoker.profiles" ), - MAVEN_OPTS( "invoker.mavenOpts" ), - FAILURE_BEHAVIOR( "invoker.failureBehavior" ), - NON_RECURSIVE( "invoker.nonRecursive" ), - OFFLINE( "invoker.offline" ), - SYSTEM_PROPERTIES_FILE( "invoker.systemPropertiesFile" ), - DEBUG( "invoker.debug" ); - - private final String key; - - private InvocationProperty( final String s ) - { - this.key = s; - } - - @Override - public String toString() - { - return key; - } - } - - private enum SelectorProperty - { - JAVA_VERSION( ".java.version" ), - MAVEN_VERSION( ".maven.version" ), - OS_FAMLY( ".os.family" ); - - private final String suffix; - - private SelectorProperty( String suffix ) - { - this.suffix = suffix; - } - - @Override - public String toString() - { - return suffix; - } - } - - /** - * The invoker properties being wrapped. - */ - private final Properties properties; - - /** - * Creates a new facade for the specified invoker properties. The properties will not be copied, so any changes to - * them will be reflected by the facade. - * - * @param properties The invoker properties to wrap, may be <code>null</code> if none. - */ - public InvokerProperties( Properties properties ) - { - this.properties = ( properties != null ) ? properties : new Properties(); - } - - /** - * Gets the invoker properties being wrapped. - * - * @return The invoker properties being wrapped, never <code>null</code>. - */ - public Properties getProperties() - { - return this.properties; - } - - /** - * Gets the name of the corresponding build job. - * - * @return The name of the build job or an empty string if not set. - */ - public String getJobName() - { - return this.properties.getProperty( "invoker.name", "" ); - } - - /** - * Gets the description of the corresponding build job. - * - * @return The description of the build job or an empty string if not set. - */ - public String getJobDescription() - { - return this.properties.getProperty( "invoker.description", "" ); - } - - /** - * Gets the specification of JRE versions on which this build job should be run. - * - * @return The specification of JRE versions or an empty string if not set. - */ - public String getJreVersion() - { - return this.properties.getProperty( "invoker.java.version", "" ); - } - - /** - * Gets the specification of JRE versions on which this build job should be run. - * - * @return The specification of JRE versions or an empty string if not set. - */ - public String getJreVersion( int index ) - { - return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.JAVA_VERSION.suffix, - getJreVersion() ); - } - - /** - * Gets the specification of Maven versions on which this build job should be run. - * - * @return The specification of Maven versions on which this build job should be run. - * @since 1.5 - */ - public String getMavenVersion() - { - return this.properties.getProperty( "invoker.maven.version", "" ); - } - - /** - * - * @param index the selector index - * @return The specification of Maven versions on which this build job should be run. - * @since 3.0.0 - */ - public String getMavenVersion( int index ) - { - return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.MAVEN_VERSION.suffix, - getMavenVersion() ); - } - - /** - * Gets the specification of OS families on which this build job should be run. - * - * @return The specification of OS families or an empty string if not set. - */ - public String getOsFamily() - { - return this.properties.getProperty( "invoker.os.family", "" ); - } - - /** - * Gets the specification of OS families on which this build job should be run. - * - * @param index the selector index - * @return The specification of OS families or an empty string if not set. - * @since 3.0.0 - */ - public String getOsFamily( int index ) - { - return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.OS_FAMLY.suffix, - getOsFamily() ); - } - - - /** - * Determines whether these invoker properties contain a build definition for the specified invocation index. - * - * @param index The one-based index of the invocation to check for, must not be negative. - * @return <code>true</code> if the invocation with the specified index is defined, <code>false</code> otherwise. - */ - public boolean isInvocationDefined( int index ) - { - for ( InvocationProperty prop : InvocationProperty.values() ) - { - if ( properties.getProperty( prop.toString() + '.' + index ) != null ) - { - return true; - } - } - return false; - } - - /** - * Determines whether these invoker properties contain a build definition for the specified selector index. - * - * @param index the index - * @return <code>true</code> if the selector with the specified index is defined, <code>false</code> otherwise. - * @since 3.0.0 - */ - public boolean isSelectorDefined( int index ) - { - for ( SelectorProperty prop : SelectorProperty.values() ) - { - if ( properties.getProperty( SELECTOR_PREFIX + index + prop.suffix ) != null ) - { - return true; - } - } - return false; - } - - /** - * Configures the specified invocation request from these invoker properties. Settings not present in the invoker - * properties will be left unchanged in the invocation request. - * - * @param request The invocation request to configure, must not be <code>null</code>. - * @param index The one-based index of the invocation to configure, must not be negative. - */ - public void configureInvocation( InvocationRequest request, int index ) - { - String project = get( InvocationProperty.PROJECT, index ); - if ( project != null ) - { - File file = new File( request.getBaseDirectory(), project ); - if ( file.isFile() ) - { - request.setBaseDirectory( file.getParentFile() ); - request.setPomFile( file ); - } - else - { - request.setBaseDirectory( file ); - request.setPomFile( null ); - } - } - - String goals = get( InvocationProperty.GOALS, index ); - if ( goals != null ) - { - request.setGoals( new ArrayList<String>( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) ); - } - - String profiles = get( InvocationProperty.PROFILES, index ); - if ( profiles != null ) - { - // CHECKSTYLE_OFF: LineLength - request.setProfiles( new ArrayList<String>( Arrays.asList( StringUtils.split( profiles, - ", \t\n\r\f" ) ) ) ); - // CHECKSTYLE_ON: LineLength - } - - String mvnOpts = get( InvocationProperty.MAVEN_OPTS, index ); - if ( mvnOpts != null ) - { - request.setMavenOpts( mvnOpts ); - } - - String failureBehavior = get( InvocationProperty.FAILURE_BEHAVIOR, index ); - if ( failureBehavior != null ) - { - ReactorFailureBehavior valueOf = - InvocationRequest.ReactorFailureBehavior.valueOfByLongOption( failureBehavior ); - request.setReactorFailureBehavior( valueOf ); - } - - String nonRecursive = get( InvocationProperty.NON_RECURSIVE, index ); - if ( nonRecursive != null ) - { - request.setRecursive( !Boolean.valueOf( nonRecursive ) ); - } - - String offline = get( InvocationProperty.OFFLINE, index ); - if ( offline != null ) - { - request.setOffline( Boolean.valueOf( offline ) ); - } - - String debug = get( InvocationProperty.DEBUG, index ); - if ( debug != null ) - { - request.setDebug( Boolean.valueOf( debug ) ); - } - } - - /** - * Checks whether the specified exit code matches the one expected for the given invocation. - * - * @param exitCode The exit code of the Maven invocation to check. - * @param index The index of the invocation for which to check the exit code, must not be negative. - * @return <code>true</code> if the exit code is zero and a success was expected or if the exit code is non-zero and - * a failue was expected, <code>false</code> otherwise. - */ - public boolean isExpectedResult( int exitCode, int index ) - { - boolean nonZeroExit = "failure".equalsIgnoreCase( get( "invoker.buildResult", index ) ); - return ( exitCode != 0 ) == nonZeroExit; - } - - /** - * Gets the path to the properties file used to set the system properties for the specified invocation. - * - * @param index The index of the invocation for which to check the exit code, must not be negative. - * @return The path to the properties file or <code>null</code> if not set. - */ - public String getSystemPropertiesFile( int index ) - { - return get( InvocationProperty.SYSTEM_PROPERTIES_FILE, index ); - } - - /** - * Gets a value from the invoker properties. The invoker properties are intended to describe the invocation settings - * for multiple builds of the same project. For this reason, the properties are indexed. First, a property named - * <code>key.index</code> will be queried. If this property does not exist, the value of the property named - * <code>key</code> will finally be returned. - * - * @param key The (base) key for the invoker property to lookup, must not be <code>null</code>. - * @param index The index of the invocation for which to retrieve the value, must not be negative. - * @return The value for the requested invoker property or <code>null</code> if not defined. - */ - String get( String key, int index ) - { - if ( index < 0 ) - { - throw new IllegalArgumentException( "invalid invocation index: " + index ); - } - - String value = properties.getProperty( key + '.' + index ); - if ( value == null ) - { - value = properties.getProperty( key ); - } - return value; - } - - private String get( InvocationProperty prop, int index ) - { - return get( prop.toString(), index ); - } -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Properties; + +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.InvocationRequest.ReactorFailureBehavior; +import org.codehaus.plexus.util.StringUtils; + +/** + * Provides a convenient facade around the <code>invoker.properties</code>. + * + * @author Benjamin Bentmann + * @version $Id: InvokerProperties.java 1779250 2017-01-17 20:20:02Z rfscholte $ + */ +class InvokerProperties +{ + private static final String SELECTOR_PREFIX = "selector."; + + private enum InvocationProperty + { + PROJECT( "invoker.project" ), + GOALS( "invoker.goals" ), + PROFILES( "invoker.profiles" ), + MAVEN_OPTS( "invoker.mavenOpts" ), + FAILURE_BEHAVIOR( "invoker.failureBehavior" ), + NON_RECURSIVE( "invoker.nonRecursive" ), + OFFLINE( "invoker.offline" ), + SYSTEM_PROPERTIES_FILE( "invoker.systemPropertiesFile" ), + DEBUG( "invoker.debug" ); + + private final String key; + + private InvocationProperty( final String s ) + { + this.key = s; + } + + @Override + public String toString() + { + return key; + } + } + + private enum SelectorProperty + { + JAVA_VERSION( ".java.version" ), + MAVEN_VERSION( ".maven.version" ), + OS_FAMLY( ".os.family" ); + + private final String suffix; + + private SelectorProperty( String suffix ) + { + this.suffix = suffix; + } + + @Override + public String toString() + { + return suffix; + } + } + + /** + * The invoker properties being wrapped. + */ + private final Properties properties; + + /** + * Creates a new facade for the specified invoker properties. The properties will not be copied, so any changes to + * them will be reflected by the facade. + * + * @param properties The invoker properties to wrap, may be <code>null</code> if none. + */ + public InvokerProperties( Properties properties ) + { + this.properties = ( properties != null ) ? properties : new Properties(); + } + + /** + * Gets the invoker properties being wrapped. + * + * @return The invoker properties being wrapped, never <code>null</code>. + */ + public Properties getProperties() + { + return this.properties; + } + + /** + * Gets the name of the corresponding build job. + * + * @return The name of the build job or an empty string if not set. + */ + public String getJobName() + { + return this.properties.getProperty( "invoker.name", "" ); + } + + /** + * Gets the description of the corresponding build job. + * + * @return The description of the build job or an empty string if not set. + */ + public String getJobDescription() + { + return this.properties.getProperty( "invoker.description", "" ); + } + + /** + * Gets the specification of JRE versions on which this build job should be run. + * + * @return The specification of JRE versions or an empty string if not set. + */ + public String getJreVersion() + { + return this.properties.getProperty( "invoker.java.version", "" ); + } + + /** + * Gets the specification of JRE versions on which this build job should be run. + * + * @return The specification of JRE versions or an empty string if not set. + */ + public String getJreVersion( int index ) + { + return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.JAVA_VERSION.suffix, + getJreVersion() ); + } + + /** + * Gets the specification of Maven versions on which this build job should be run. + * + * @return The specification of Maven versions on which this build job should be run. + * @since 1.5 + */ + public String getMavenVersion() + { + return this.properties.getProperty( "invoker.maven.version", "" ); + } + + /** + * + * @param index the selector index + * @return The specification of Maven versions on which this build job should be run. + * @since 3.0.0 + */ + public String getMavenVersion( int index ) + { + return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.MAVEN_VERSION.suffix, + getMavenVersion() ); + } + + /** + * Gets the specification of OS families on which this build job should be run. + * + * @return The specification of OS families or an empty string if not set. + */ + public String getOsFamily() + { + return this.properties.getProperty( "invoker.os.family", "" ); + } + + /** + * Gets the specification of OS families on which this build job should be run. + * + * @param index the selector index + * @return The specification of OS families or an empty string if not set. + * @since 3.0.0 + */ + public String getOsFamily( int index ) + { + return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.OS_FAMLY.suffix, + getOsFamily() ); + } + + + /** + * Determines whether these invoker properties contain a build definition for the specified invocation index. + * + * @param index The one-based index of the invocation to check for, must not be negative. + * @return <code>true</code> if the invocation with the specified index is defined, <code>false</code> otherwise. + */ + public boolean isInvocationDefined( int index ) + { + for ( InvocationProperty prop : InvocationProperty.values() ) + { + if ( properties.getProperty( prop.toString() + '.' + index ) != null ) + { + return true; + } + } + return false; + } + + /** + * Determines whether these invoker properties contain a build definition for the specified selector index. + * + * @param index the index + * @return <code>true</code> if the selector with the specified index is defined, <code>false</code> otherwise. + * @since 3.0.0 + */ + public boolean isSelectorDefined( int index ) + { + for ( SelectorProperty prop : SelectorProperty.values() ) + { + if ( properties.getProperty( SELECTOR_PREFIX + index + prop.suffix ) != null ) + { + return true; + } + } + return false; + } + + /** + * Configures the specified invocation request from these invoker properties. Settings not present in the invoker + * properties will be left unchanged in the invocation request. + * + * @param request The invocation request to configure, must not be <code>null</code>. + * @param index The one-based index of the invocation to configure, must not be negative. + */ + public void configureInvocation( InvocationRequest request, int index ) + { + String project = get( InvocationProperty.PROJECT, index ); + if ( project != null ) + { + File file = new File( request.getBaseDirectory(), project ); + if ( file.isFile() ) + { + request.setBaseDirectory( file.getParentFile() ); + request.setPomFile( file ); + } + else + { + request.setBaseDirectory( file ); + request.setPomFile( null ); + } + } + + String goals = get( InvocationProperty.GOALS, index ); + if ( goals != null ) + { + request.setGoals( new ArrayList<String>( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) ); + } + + String profiles = get( InvocationProperty.PROFILES, index ); + if ( profiles != null ) + { + // CHECKSTYLE_OFF: LineLength + request.setProfiles( new ArrayList<String>( Arrays.asList( StringUtils.split( profiles, + ", \t\n\r\f" ) ) ) ); + // CHECKSTYLE_ON: LineLength + } + + String mvnOpts = get( InvocationProperty.MAVEN_OPTS, index ); + if ( mvnOpts != null ) + { + request.setMavenOpts( mvnOpts ); + } + + String failureBehavior = get( InvocationProperty.FAILURE_BEHAVIOR, index ); + if ( failureBehavior != null ) + { + ReactorFailureBehavior valueOf = + InvocationRequest.ReactorFailureBehavior.valueOfByLongOption( failureBehavior ); + request.setReactorFailureBehavior( valueOf ); + } + + String nonRecursive = get( InvocationProperty.NON_RECURSIVE, index ); + if ( nonRecursive != null ) + { + request.setRecursive( !Boolean.valueOf( nonRecursive ) ); + } + + String offline = get( InvocationProperty.OFFLINE, index ); + if ( offline != null ) + { + request.setOffline( Boolean.valueOf( offline ) ); + } + + String debug = get( InvocationProperty.DEBUG, index ); + if ( debug != null ) + { + request.setDebug( Boolean.valueOf( debug ) ); + } + } + + /** + * Checks whether the specified exit code matches the one expected for the given invocation. + * + * @param exitCode The exit code of the Maven invocation to check. + * @param index The index of the invocation for which to check the exit code, must not be negative. + * @return <code>true</code> if the exit code is zero and a success was expected or if the exit code is non-zero and + * a failue was expected, <code>false</code> otherwise. + */ + public boolean isExpectedResult( int exitCode, int index ) + { + boolean nonZeroExit = "failure".equalsIgnoreCase( get( "invoker.buildResult", index ) ); + return ( exitCode != 0 ) == nonZeroExit; + } + + /** + * Gets the path to the properties file used to set the system properties for the specified invocation. + * + * @param index The index of the invocation for which to check the exit code, must not be negative. + * @return The path to the properties file or <code>null</code> if not set. + */ + public String getSystemPropertiesFile( int index ) + { + return get( InvocationProperty.SYSTEM_PROPERTIES_FILE, index ); + } + + /** + * Gets a value from the invoker properties. The invoker properties are intended to describe the invocation settings + * for multiple builds of the same project. For this reason, the properties are indexed. First, a property named + * <code>key.index</code> will be queried. If this property does not exist, the value of the property named + * <code>key</code> will finally be returned. + * + * @param key The (base) key for the invoker property to lookup, must not be <code>null</code>. + * @param index The index of the invocation for which to retrieve the value, must not be negative. + * @return The value for the requested invoker property or <code>null</code> if not defined. + */ + String get( String key, int index ) + { + if ( index < 0 ) + { + throw new IllegalArgumentException( "invalid invocation index: " + index ); + } + + String value = properties.getProperty( key + '.' + index ); + if ( value == null ) + { + value = properties.getProperty( key ); + } + return value; + } + + private String get( InvocationProperty prop, int index ) + { + return get( prop.toString(), index ); + } +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/InvokerReport.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java similarity index 98% rename from src/main/java/org/apache/maven/plugin/invoker/InvokerReport.java rename to src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java index 7792974..95caf97 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/InvokerReport.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerReport.java
@@ -1,357 +1,357 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.IOException; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.apache.maven.doxia.sink.Sink; -import org.apache.maven.doxia.siterenderer.Renderer; -import org.apache.maven.plugin.invoker.model.BuildJob; -import org.apache.maven.plugin.invoker.model.io.xpp3.BuildJobXpp3Reader; -import org.apache.maven.plugins.annotations.Component; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; -import org.apache.maven.reporting.AbstractMavenReport; -import org.apache.maven.reporting.MavenReportException; -import org.codehaus.plexus.i18n.I18N; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -/** - * Generate a report based on the results of the Maven invocations. <strong>Note:</strong> This mojo doesn't fork any - * lifecycle, if you have a clean working copy, you have to use a command like - * <code>mvn clean integration-test site</code> to ensure the build results are present when this goal is invoked. - * - * @author Olivier Lamy - * @since 1.4 - */ -@Mojo( name = "report", threadSafe = true ) -public class InvokerReport - extends AbstractMavenReport -{ - - /** - * The Maven Project. - */ - @Parameter( defaultValue = "${project}", readonly = true, required = true ) - protected MavenProject project; - - /** - * Doxia Site Renderer component. - */ - @Component - protected Renderer siteRenderer; - - /** - * Internationalization component. - */ - @Component - protected I18N i18n; - - /** - * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from - * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in - * the Maven Site Plugin is used instead. - */ - @Parameter( defaultValue = "${project.reporting.outputDirectory}", required = true ) - protected File outputDirectory; - - /** - * Base directory where all build reports have been written to. - */ - @Parameter( defaultValue = "${project.build.directory}/invoker-reports", property = "invoker.reportsDirectory" ) - private File reportsDirectory; - - /** - * The number format used to print percent values in the report locale. - */ - private NumberFormat percentFormat; - - /** - * The number format used to print time values in the report locale. - */ - private NumberFormat secondsFormat; - - protected void executeReport( Locale locale ) - throws MavenReportException - { - DecimalFormatSymbols symbols = new DecimalFormatSymbols( locale ); - percentFormat = new DecimalFormat( getText( locale, "report.invoker.format.percent" ), symbols ); - secondsFormat = new DecimalFormat( getText( locale, "report.invoker.format.seconds" ), symbols ); - - Sink sink = getSink(); - - sink.head(); - - sink.title(); - sink.text( getText( locale, "report.invoker.result.title" ) ); - sink.title_(); - - sink.head_(); - - sink.body(); - - sink.section1(); - sink.sectionTitle1(); - sink.text( getText( locale, "report.invoker.result.title" ) ); - sink.sectionTitle1_(); - sink.paragraph(); - sink.text( getText( locale, "report.invoker.result.description" ) ); - sink.paragraph_(); - sink.section1_(); - - // ---------------------------------- - // build buildJob beans - // ---------------------------------- - File[] reportFiles = ReportUtils.getReportFiles( reportsDirectory ); - if ( reportFiles.length <= 0 ) - { - getLog().info( "no invoker report files found, skip report generation" ); - return; - } - - List<BuildJob> buildJobs = new ArrayList<BuildJob>( reportFiles.length ); - for ( File reportFile : reportFiles ) - { - try - { - BuildJobXpp3Reader reader = new BuildJobXpp3Reader(); - buildJobs.add( reader.read( ReaderFactory.newXmlReader( reportFile ) ) ); - } - catch ( XmlPullParserException e ) - { - throw new MavenReportException( "Failed to parse report file: " + reportFile, e ); - } - catch ( IOException e ) - { - throw new MavenReportException( "Failed to read report file: " + reportFile, e ); - } - } - - // ---------------------------------- - // summary - // ---------------------------------- - - constructSummarySection( buildJobs, locale ); - - // ---------------------------------- - // per file/it detail - // ---------------------------------- - - sink.section2(); - sink.sectionTitle2(); - - sink.text( getText( locale, "report.invoker.detail.title" ) ); - - sink.sectionTitle2_(); - - sink.section2_(); - - // detail tests table header - sink.table(); - - sink.tableRow(); - // ------------------------------------------- - // name | Result | time | message - // ------------------------------------------- - sinkTableHeader( sink, getText( locale, "report.invoker.detail.name" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.detail.result" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.detail.time" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.detail.message" ) ); - - sink.tableRow_(); - - for ( BuildJob buildJob : buildJobs ) - { - renderBuildJob( buildJob, locale ); - } - - sink.table_(); - - sink.body_(); - - sink.flush(); - sink.close(); - } - - private void constructSummarySection( List<? extends BuildJob> buildJobs, Locale locale ) - { - Sink sink = getSink(); - - sink.section2(); - sink.sectionTitle2(); - - sink.text( getText( locale, "report.invoker.summary.title" ) ); - - sink.sectionTitle2_(); - sink.section2_(); - - // ------------------------------------------------------------------------ - // Building a table with - // it number | succes nb | failed nb | Success rate | total time | avg time - // ------------------------------------------------------------------------ - - sink.table(); - sink.tableRow(); - - sinkTableHeader( sink, getText( locale, "report.invoker.summary.number" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.success" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.failed" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.skipped" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.success.rate" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.time.total" ) ); - sinkTableHeader( sink, getText( locale, "report.invoker.summary.time.avg" ) ); - - int number = buildJobs.size(); - int success = 0; - int failed = 0; - int skipped = 0; - double totalTime = 0; - - for ( BuildJob buildJob : buildJobs ) - { - if ( BuildJob.Result.SUCCESS.equals( buildJob.getResult() ) ) - { - success++; - } - else if ( BuildJob.Result.SKIPPED.equals( buildJob.getResult() ) ) - { - skipped++; - } - else - { - failed++; - } - totalTime += buildJob.getTime(); - } - - sink.tableRow_(); - sink.tableRow(); - - sinkCell( sink, Integer.toString( number ) ); - sinkCell( sink, Integer.toString( success ) ); - sinkCell( sink, Integer.toString( failed ) ); - sinkCell( sink, Integer.toString( skipped ) ); - - if ( success + failed > 0 ) - { - sinkCell( sink, percentFormat.format( (double) success / ( success + failed ) ) ); - } - else - { - sinkCell( sink, "" ); - } - - sinkCell( sink, secondsFormat.format( totalTime ) ); - - sinkCell( sink, secondsFormat.format( totalTime / number ) ); - - sink.tableRow_(); - sink.table_(); - - } - - private void renderBuildJob( BuildJob buildJob, Locale locale ) - { - Sink sink = getSink(); - sink.tableRow(); - StringBuilder buffer = new StringBuilder(); - if ( !StringUtils.isEmpty( buildJob.getName() ) && !StringUtils.isEmpty( buildJob.getDescription() ) ) - { - buffer.append( buildJob.getName() ); - buffer.append( " : " ); - buffer.append( buildJob.getDescription() ); - } - else - { - buffer.append( buildJob.getProject() ); - } - sinkCell( sink, buffer.toString() ); - // FIXME image - sinkCell( sink, buildJob.getResult() ); - sinkCell( sink, secondsFormat.format( buildJob.getTime() ) ); - sinkCell( sink, buildJob.getFailureMessage() ); - sink.tableRow_(); - } - - protected String getOutputDirectory() - { - return outputDirectory.getAbsolutePath(); - } - - protected MavenProject getProject() - { - return project; - } - - protected Renderer getSiteRenderer() - { - return siteRenderer; - } - - public String getDescription( Locale locale ) - { - return getText( locale, "report.invoker.result.description" ); - } - - public String getName( Locale locale ) - { - return getText( locale, "report.invoker.result.name" ); - } - - public String getOutputName() - { - return "invoker-report"; - } - - public boolean canGenerateReport() - { - return ReportUtils.getReportFiles( reportsDirectory ).length > 0; - } - - private String getText( Locale locale, String key ) - { - return i18n.getString( "invoker-report", locale, key ); - } - - private void sinkTableHeader( Sink sink, String header ) - { - sink.tableHeaderCell(); - sink.text( header ); - sink.tableHeaderCell_(); - } - - private void sinkCell( Sink sink, String text ) - { - sink.tableCell(); - sink.text( text ); - sink.tableCell_(); - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.apache.maven.doxia.sink.Sink; +import org.apache.maven.doxia.siterenderer.Renderer; +import org.apache.maven.plugins.invoker.model.BuildJob; +import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Reader; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.apache.maven.reporting.AbstractMavenReport; +import org.apache.maven.reporting.MavenReportException; +import org.codehaus.plexus.i18n.I18N; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Generate a report based on the results of the Maven invocations. <strong>Note:</strong> This mojo doesn't fork any + * lifecycle, if you have a clean working copy, you have to use a command like + * <code>mvn clean integration-test site</code> to ensure the build results are present when this goal is invoked. + * + * @author Olivier Lamy + * @since 1.4 + */ +@Mojo( name = "report", threadSafe = true ) +public class InvokerReport + extends AbstractMavenReport +{ + + /** + * The Maven Project. + */ + @Parameter( defaultValue = "${project}", readonly = true, required = true ) + protected MavenProject project; + + /** + * Doxia Site Renderer component. + */ + @Component + protected Renderer siteRenderer; + + /** + * Internationalization component. + */ + @Component + protected I18N i18n; + + /** + * The output directory for the report. Note that this parameter is only evaluated if the goal is run directly from + * the command line. If the goal is run indirectly as part of a site generation, the output directory configured in + * the Maven Site Plugin is used instead. + */ + @Parameter( defaultValue = "${project.reporting.outputDirectory}", required = true ) + protected File outputDirectory; + + /** + * Base directory where all build reports have been written to. + */ + @Parameter( defaultValue = "${project.build.directory}/invoker-reports", property = "invoker.reportsDirectory" ) + private File reportsDirectory; + + /** + * The number format used to print percent values in the report locale. + */ + private NumberFormat percentFormat; + + /** + * The number format used to print time values in the report locale. + */ + private NumberFormat secondsFormat; + + protected void executeReport( Locale locale ) + throws MavenReportException + { + DecimalFormatSymbols symbols = new DecimalFormatSymbols( locale ); + percentFormat = new DecimalFormat( getText( locale, "report.invoker.format.percent" ), symbols ); + secondsFormat = new DecimalFormat( getText( locale, "report.invoker.format.seconds" ), symbols ); + + Sink sink = getSink(); + + sink.head(); + + sink.title(); + sink.text( getText( locale, "report.invoker.result.title" ) ); + sink.title_(); + + sink.head_(); + + sink.body(); + + sink.section1(); + sink.sectionTitle1(); + sink.text( getText( locale, "report.invoker.result.title" ) ); + sink.sectionTitle1_(); + sink.paragraph(); + sink.text( getText( locale, "report.invoker.result.description" ) ); + sink.paragraph_(); + sink.section1_(); + + // ---------------------------------- + // build buildJob beans + // ---------------------------------- + File[] reportFiles = ReportUtils.getReportFiles( reportsDirectory ); + if ( reportFiles.length <= 0 ) + { + getLog().info( "no invoker report files found, skip report generation" ); + return; + } + + List<BuildJob> buildJobs = new ArrayList<BuildJob>( reportFiles.length ); + for ( File reportFile : reportFiles ) + { + try + { + BuildJobXpp3Reader reader = new BuildJobXpp3Reader(); + buildJobs.add( reader.read( ReaderFactory.newXmlReader( reportFile ) ) ); + } + catch ( XmlPullParserException e ) + { + throw new MavenReportException( "Failed to parse report file: " + reportFile, e ); + } + catch ( IOException e ) + { + throw new MavenReportException( "Failed to read report file: " + reportFile, e ); + } + } + + // ---------------------------------- + // summary + // ---------------------------------- + + constructSummarySection( buildJobs, locale ); + + // ---------------------------------- + // per file/it detail + // ---------------------------------- + + sink.section2(); + sink.sectionTitle2(); + + sink.text( getText( locale, "report.invoker.detail.title" ) ); + + sink.sectionTitle2_(); + + sink.section2_(); + + // detail tests table header + sink.table(); + + sink.tableRow(); + // ------------------------------------------- + // name | Result | time | message + // ------------------------------------------- + sinkTableHeader( sink, getText( locale, "report.invoker.detail.name" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.detail.result" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.detail.time" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.detail.message" ) ); + + sink.tableRow_(); + + for ( BuildJob buildJob : buildJobs ) + { + renderBuildJob( buildJob, locale ); + } + + sink.table_(); + + sink.body_(); + + sink.flush(); + sink.close(); + } + + private void constructSummarySection( List<? extends BuildJob> buildJobs, Locale locale ) + { + Sink sink = getSink(); + + sink.section2(); + sink.sectionTitle2(); + + sink.text( getText( locale, "report.invoker.summary.title" ) ); + + sink.sectionTitle2_(); + sink.section2_(); + + // ------------------------------------------------------------------------ + // Building a table with + // it number | succes nb | failed nb | Success rate | total time | avg time + // ------------------------------------------------------------------------ + + sink.table(); + sink.tableRow(); + + sinkTableHeader( sink, getText( locale, "report.invoker.summary.number" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.success" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.failed" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.skipped" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.success.rate" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.time.total" ) ); + sinkTableHeader( sink, getText( locale, "report.invoker.summary.time.avg" ) ); + + int number = buildJobs.size(); + int success = 0; + int failed = 0; + int skipped = 0; + double totalTime = 0; + + for ( BuildJob buildJob : buildJobs ) + { + if ( BuildJob.Result.SUCCESS.equals( buildJob.getResult() ) ) + { + success++; + } + else if ( BuildJob.Result.SKIPPED.equals( buildJob.getResult() ) ) + { + skipped++; + } + else + { + failed++; + } + totalTime += buildJob.getTime(); + } + + sink.tableRow_(); + sink.tableRow(); + + sinkCell( sink, Integer.toString( number ) ); + sinkCell( sink, Integer.toString( success ) ); + sinkCell( sink, Integer.toString( failed ) ); + sinkCell( sink, Integer.toString( skipped ) ); + + if ( success + failed > 0 ) + { + sinkCell( sink, percentFormat.format( (double) success / ( success + failed ) ) ); + } + else + { + sinkCell( sink, "" ); + } + + sinkCell( sink, secondsFormat.format( totalTime ) ); + + sinkCell( sink, secondsFormat.format( totalTime / number ) ); + + sink.tableRow_(); + sink.table_(); + + } + + private void renderBuildJob( BuildJob buildJob, Locale locale ) + { + Sink sink = getSink(); + sink.tableRow(); + StringBuilder buffer = new StringBuilder(); + if ( !StringUtils.isEmpty( buildJob.getName() ) && !StringUtils.isEmpty( buildJob.getDescription() ) ) + { + buffer.append( buildJob.getName() ); + buffer.append( " : " ); + buffer.append( buildJob.getDescription() ); + } + else + { + buffer.append( buildJob.getProject() ); + } + sinkCell( sink, buffer.toString() ); + // FIXME image + sinkCell( sink, buildJob.getResult() ); + sinkCell( sink, secondsFormat.format( buildJob.getTime() ) ); + sinkCell( sink, buildJob.getFailureMessage() ); + sink.tableRow_(); + } + + protected String getOutputDirectory() + { + return outputDirectory.getAbsolutePath(); + } + + protected MavenProject getProject() + { + return project; + } + + protected Renderer getSiteRenderer() + { + return siteRenderer; + } + + public String getDescription( Locale locale ) + { + return getText( locale, "report.invoker.result.description" ); + } + + public String getName( Locale locale ) + { + return getText( locale, "report.invoker.result.name" ); + } + + public String getOutputName() + { + return "invoker-report"; + } + + public boolean canGenerateReport() + { + return ReportUtils.getReportFiles( reportsDirectory ).length > 0; + } + + private String getText( Locale locale, String key ) + { + return i18n.getString( "invoker-report", locale, key ); + } + + private void sinkTableHeader( Sink sink, String header ) + { + sink.tableHeaderCell(); + sink.text( header ); + sink.tableHeaderCell_(); + } + + private void sinkCell( Sink sink, String text ) + { + sink.tableCell(); + sink.text( text ); + sink.tableCell_(); + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/InvokerSession.java b/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java similarity index 98% rename from src/main/java/org/apache/maven/plugin/invoker/InvokerSession.java rename to src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java index de95329..aa617da 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/InvokerSession.java +++ b/src/main/java/org/apache/maven/plugins/invoker/InvokerSession.java
@@ -1,279 +1,279 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.invoker.model.BuildJob; -import org.apache.maven.plugin.logging.Log; - -/** - * Tracks a set of build jobs and their results. - * - * @author Benjamin Bentmann - */ -class InvokerSession -{ - - private List<BuildJob> buildJobs; - - private List<BuildJob> failedJobs; - - private List<BuildJob> errorJobs; - - private List<BuildJob> successfulJobs; - - private List<BuildJob> skippedJobs; - - /** - * Creates a new empty session. - */ - public InvokerSession() - { - buildJobs = new ArrayList<BuildJob>(); - } - - /** - * Creates a session that initially contains the specified build jobs. - * - * @param buildJobs The build jobs to set, must not be <code>null</code>. - */ - public InvokerSession( BuildJob[] buildJobs ) - { - this.buildJobs = new ArrayList<BuildJob>( Arrays.asList( buildJobs ) ); - } - - /** - * Adds the specified build job to this session. - * - * @param buildJob The build job to add, must not be <code>null</code>. - */ - public void addJob( BuildJob buildJob ) - { - buildJobs.add( buildJob ); - - resetStats(); - } - - /** - * Sets the build jobs of this session. - * - * @param buildJobs The build jobs to set, must not be <code>null</code>. - */ - public void setJobs( List<? extends BuildJob> buildJobs ) - { - this.buildJobs = new ArrayList<BuildJob>( buildJobs ); - - resetStats(); - } - - /** - * Gets the build jobs in this session. - * - * @return The build jobs in this session, can be empty but never <code>null</code>. - */ - public List<BuildJob> getJobs() - { - return buildJobs; - } - - /** - * Gets the successful build jobs in this session. - * - * @return The successful build jobs in this session, can be empty but never <code>null</code>. - */ - public List<BuildJob> getSuccessfulJobs() - { - updateStats(); - - return successfulJobs; - } - - /** - * Gets the failed build jobs in this session. - * - * @return The failed build jobs in this session, can be empty but never <code>null</code>. - */ - public List<BuildJob> getFailedJobs() - { - updateStats(); - - return failedJobs; - } - - /** - * Gets the build jobs which had errors for this session. - * - * @return The build jobs in error for this session, can be empty but never <code>null</code>. - */ - public List<BuildJob> getErrorJobs() - { - updateStats(); - - return errorJobs; - } - - /** - * Gets the skipped build jobs in this session. - * - * @return The skipped build jobs in this session, can be empty but never <code>null</code>. - */ - public List<BuildJob> getSkippedJobs() - { - updateStats(); - - return skippedJobs; - } - - private void resetStats() - { - successfulJobs = null; - failedJobs = null; - skippedJobs = null; - errorJobs = null; - } - - private void updateStats() - { - if ( successfulJobs != null && skippedJobs != null && failedJobs != null && errorJobs != null ) - { - return; - } - - successfulJobs = new ArrayList<BuildJob>(); - failedJobs = new ArrayList<BuildJob>(); - skippedJobs = new ArrayList<BuildJob>(); - errorJobs = new ArrayList<BuildJob>(); - - for ( BuildJob buildJob : buildJobs ) - { - if ( BuildJob.Result.SUCCESS.equals( buildJob.getResult() ) ) - { - successfulJobs.add( buildJob ); - } - else if ( BuildJob.Result.SKIPPED.equals( buildJob.getResult() ) ) - { - skippedJobs.add( buildJob ); - } - else if ( BuildJob.Result.ERROR.equals( buildJob.getResult() ) ) - { - errorJobs.add( buildJob ); - } - else if ( buildJob.getResult() != null ) - { - failedJobs.add( buildJob ); - } - } - } - - /** - * Prints a summary of this session to the specified logger. - * - * @param logger The mojo logger to output messages to, must not be <code>null</code>. - * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled. - */ - public void logSummary( Log logger, boolean ignoreFailures ) - { - updateStats(); - - String separator = buffer().strong( "-------------------------------------------------" ).toString(); - - logger.info( separator ); - logger.info( "Build Summary:" ); - logger.info( " Passed: " + successfulJobs.size() + ", Failed: " + failedJobs.size() + ", Errors: " - + errorJobs.size() + ", Skipped: " + skippedJobs.size() ); - logger.info( separator ); - - if ( !failedJobs.isEmpty() ) - { - String heading = "The following builds failed:"; - if ( ignoreFailures ) - { - logger.warn( heading ); - } - else - { - logger.error( heading ); - } - - for ( BuildJob buildJob : failedJobs ) - { - String item = "* " + buildJob.getProject(); - if ( ignoreFailures ) - { - logger.warn( item ); - } - else - { - logger.error( item ); - } - } - - logger.info( separator ); - } - } - - /** - * Handles the build failures in this session. - * - * @param logger The mojo logger to output messages to, must not be <code>null</code>. - * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled. - * @throws MojoFailureException If failures are present and not ignored. - */ - public void handleFailures( Log logger, boolean ignoreFailures ) - throws MojoFailureException - { - updateStats(); - - if ( !failedJobs.isEmpty() ) - { - String message = failedJobs.size() + " build" + ( failedJobs.size() == 1 ? "" : "s" ) + " failed."; - - if ( ignoreFailures ) - { - logger.warn( "Ignoring that " + message ); - } - else - { - throw new MojoFailureException( message + " See console output above for details." ); - } - } - - if ( !errorJobs.isEmpty() ) - { - String message = errorJobs.size() + " build" + ( errorJobs.size() == 1 ? "" : "s" ) + " in error."; - - if ( ignoreFailures ) - { - logger.warn( "Ignoring that " + message ); - } - else - { - throw new MojoFailureException( message + " See console output above for details." ); - } - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import static org.apache.maven.shared.utils.logging.MessageUtils.buffer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.invoker.model.BuildJob; +import org.apache.maven.plugin.logging.Log; + +/** + * Tracks a set of build jobs and their results. + * + * @author Benjamin Bentmann + */ +class InvokerSession +{ + + private List<BuildJob> buildJobs; + + private List<BuildJob> failedJobs; + + private List<BuildJob> errorJobs; + + private List<BuildJob> successfulJobs; + + private List<BuildJob> skippedJobs; + + /** + * Creates a new empty session. + */ + public InvokerSession() + { + buildJobs = new ArrayList<BuildJob>(); + } + + /** + * Creates a session that initially contains the specified build jobs. + * + * @param buildJobs The build jobs to set, must not be <code>null</code>. + */ + public InvokerSession( BuildJob[] buildJobs ) + { + this.buildJobs = new ArrayList<BuildJob>( Arrays.asList( buildJobs ) ); + } + + /** + * Adds the specified build job to this session. + * + * @param buildJob The build job to add, must not be <code>null</code>. + */ + public void addJob( BuildJob buildJob ) + { + buildJobs.add( buildJob ); + + resetStats(); + } + + /** + * Sets the build jobs of this session. + * + * @param buildJobs The build jobs to set, must not be <code>null</code>. + */ + public void setJobs( List<? extends BuildJob> buildJobs ) + { + this.buildJobs = new ArrayList<BuildJob>( buildJobs ); + + resetStats(); + } + + /** + * Gets the build jobs in this session. + * + * @return The build jobs in this session, can be empty but never <code>null</code>. + */ + public List<BuildJob> getJobs() + { + return buildJobs; + } + + /** + * Gets the successful build jobs in this session. + * + * @return The successful build jobs in this session, can be empty but never <code>null</code>. + */ + public List<BuildJob> getSuccessfulJobs() + { + updateStats(); + + return successfulJobs; + } + + /** + * Gets the failed build jobs in this session. + * + * @return The failed build jobs in this session, can be empty but never <code>null</code>. + */ + public List<BuildJob> getFailedJobs() + { + updateStats(); + + return failedJobs; + } + + /** + * Gets the build jobs which had errors for this session. + * + * @return The build jobs in error for this session, can be empty but never <code>null</code>. + */ + public List<BuildJob> getErrorJobs() + { + updateStats(); + + return errorJobs; + } + + /** + * Gets the skipped build jobs in this session. + * + * @return The skipped build jobs in this session, can be empty but never <code>null</code>. + */ + public List<BuildJob> getSkippedJobs() + { + updateStats(); + + return skippedJobs; + } + + private void resetStats() + { + successfulJobs = null; + failedJobs = null; + skippedJobs = null; + errorJobs = null; + } + + private void updateStats() + { + if ( successfulJobs != null && skippedJobs != null && failedJobs != null && errorJobs != null ) + { + return; + } + + successfulJobs = new ArrayList<BuildJob>(); + failedJobs = new ArrayList<BuildJob>(); + skippedJobs = new ArrayList<BuildJob>(); + errorJobs = new ArrayList<BuildJob>(); + + for ( BuildJob buildJob : buildJobs ) + { + if ( BuildJob.Result.SUCCESS.equals( buildJob.getResult() ) ) + { + successfulJobs.add( buildJob ); + } + else if ( BuildJob.Result.SKIPPED.equals( buildJob.getResult() ) ) + { + skippedJobs.add( buildJob ); + } + else if ( BuildJob.Result.ERROR.equals( buildJob.getResult() ) ) + { + errorJobs.add( buildJob ); + } + else if ( buildJob.getResult() != null ) + { + failedJobs.add( buildJob ); + } + } + } + + /** + * Prints a summary of this session to the specified logger. + * + * @param logger The mojo logger to output messages to, must not be <code>null</code>. + * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled. + */ + public void logSummary( Log logger, boolean ignoreFailures ) + { + updateStats(); + + String separator = buffer().strong( "-------------------------------------------------" ).toString(); + + logger.info( separator ); + logger.info( "Build Summary:" ); + logger.info( " Passed: " + successfulJobs.size() + ", Failed: " + failedJobs.size() + ", Errors: " + + errorJobs.size() + ", Skipped: " + skippedJobs.size() ); + logger.info( separator ); + + if ( !failedJobs.isEmpty() ) + { + String heading = "The following builds failed:"; + if ( ignoreFailures ) + { + logger.warn( heading ); + } + else + { + logger.error( heading ); + } + + for ( BuildJob buildJob : failedJobs ) + { + String item = "* " + buildJob.getProject(); + if ( ignoreFailures ) + { + logger.warn( item ); + } + else + { + logger.error( item ); + } + } + + logger.info( separator ); + } + } + + /** + * Handles the build failures in this session. + * + * @param logger The mojo logger to output messages to, must not be <code>null</code>. + * @param ignoreFailures A flag whether failures should be ignored or whether a build failure should be signaled. + * @throws MojoFailureException If failures are present and not ignored. + */ + public void handleFailures( Log logger, boolean ignoreFailures ) + throws MojoFailureException + { + updateStats(); + + if ( !failedJobs.isEmpty() ) + { + String message = failedJobs.size() + " build" + ( failedJobs.size() == 1 ? "" : "s" ) + " failed."; + + if ( ignoreFailures ) + { + logger.warn( "Ignoring that " + message ); + } + else + { + throw new MojoFailureException( message + " See console output above for details." ); + } + } + + if ( !errorJobs.isEmpty() ) + { + String message = errorJobs.size() + " build" + ( errorJobs.size() == 1 ? "" : "s" ) + " in error."; + + if ( ignoreFailures ) + { + logger.warn( "Ignoring that " + message ); + } + else + { + throw new MojoFailureException( message + " See console output above for details." ); + } + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/MetadataUtils.java b/src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java similarity index 99% rename from src/main/java/org/apache/maven/plugin/invoker/MetadataUtils.java rename to src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java index d353406..23ce866 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/MetadataUtils.java +++ b/src/main/java/org/apache/maven/plugins/invoker/MetadataUtils.java
@@ -1,186 +1,186 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.text.SimpleDateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TimeZone; - -import org.apache.maven.artifact.Artifact; -import org.codehaus.plexus.util.IOUtil; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.WriterFactory; -import org.codehaus.plexus.util.xml.Xpp3Dom; -import org.codehaus.plexus.util.xml.Xpp3DomBuilder; -import org.codehaus.plexus.util.xml.Xpp3DomUtils; -import org.codehaus.plexus.util.xml.Xpp3DomWriter; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -/** - * Provides utility methods for artifact metadata processing. - * - * @author Benjamin Bentmann - */ -class MetadataUtils -{ - - /** - * Creates local metadata files for the specified artifact. The goal is to simulate the installation of the artifact - * by a local build, thereby decoupling the forked builds from the inderministic collection of remote repositories - * that are available to the main build and from which the artifact was originally resolved. - * - * @param file The artifact's file in the local test repository, must not be <code>null</code>. - * @param artifact The artifact to create metadata for, must not be <code>null</code>. - * @throws IOException If the metadata could not be created. - */ - public static void createMetadata( File file, Artifact artifact ) - throws IOException - { - TimeZone tz = java.util.TimeZone.getTimeZone( "UTC" ); - SimpleDateFormat fmt = new SimpleDateFormat( "yyyyMMddHHmmss" ); - fmt.setTimeZone( tz ); - String timestamp = fmt.format( new Date() ); - - if ( artifact.isSnapshot() ) - { - File metadataFile = new File( file.getParentFile(), "maven-metadata-local.xml" ); - - Xpp3Dom metadata = new Xpp3Dom( "metadata" ); - addChild( metadata, "groupId", artifact.getGroupId() ); - addChild( metadata, "artifactId", artifact.getArtifactId() ); - addChild( metadata, "version", artifact.getBaseVersion() ); - Xpp3Dom versioning = new Xpp3Dom( "versioning" ); - versioning.addChild( addChild( new Xpp3Dom( "snapshot" ), "localCopy", "true" ) ); - addChild( versioning, "lastUpdated", timestamp ); - metadata.addChild( versioning ); - - writeMetadata( metadataFile, metadata ); - } - - File metadataFile = new File( file.getParentFile().getParentFile(), "maven-metadata-local.xml" ); - - Set<String> allVersions = new LinkedHashSet<String>(); - - Xpp3Dom metadata = readMetadata( metadataFile ); - - if ( metadata != null ) - { - Xpp3Dom versioning = metadata.getChild( "versioning" ); - if ( versioning != null ) - { - Xpp3Dom versions = versioning.getChild( "versions" ); - if ( versions != null ) - { - - Xpp3Dom[] children = versions.getChildren( "version" ); - for ( Xpp3Dom aChildren : children ) - { - allVersions.add( aChildren.getValue() ); - } - } - } - } - - allVersions.add( artifact.getBaseVersion() ); - - metadata = new Xpp3Dom( "metadata" ); - addChild( metadata, "groupId", artifact.getGroupId() ); - addChild( metadata, "artifactId", artifact.getArtifactId() ); - Xpp3Dom versioning = new Xpp3Dom( "versioning" ); - versioning.addChild( addChildren( new Xpp3Dom( "versions" ), "version", allVersions ) ); - addChild( versioning, "lastUpdated", timestamp ); - metadata.addChild( versioning ); - - metadata = Xpp3DomUtils.mergeXpp3Dom( metadata, readMetadata( metadataFile ) ); - - writeMetadata( metadataFile, metadata ); - } - - private static Xpp3Dom addChild( Xpp3Dom parent, String childName, String childValue ) - { - Xpp3Dom child = new Xpp3Dom( childName ); - child.setValue( childValue ); - parent.addChild( child ); - return parent; - } - - private static Xpp3Dom addChildren( Xpp3Dom parent, String childName, Collection<String> childValues ) - { - for ( String childValue : childValues ) - { - addChild( parent, childName, childValue ); - } - return parent; - } - - private static Xpp3Dom readMetadata( File metadataFile ) - throws IOException - { - if ( !metadataFile.isFile() ) - { - return null; - } - - Reader reader = null; - try - { - reader = ReaderFactory.newXmlReader( metadataFile ); - final Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( reader ); - reader.close(); - reader = null; - return xpp3Dom; - } - catch ( XmlPullParserException e ) - { - throw (IOException) new IOException( e.getMessage() ).initCause( e ); - } - finally - { - IOUtil.close( reader ); - } - } - - private static void writeMetadata( File metadataFile, Xpp3Dom metadata ) - throws IOException - { - metadataFile.getParentFile().mkdirs(); - - Writer writer = null; - try - { - writer = WriterFactory.newXmlWriter( metadataFile ); - Xpp3DomWriter.write( writer, metadata ); - writer.close(); - writer = null; - } - finally - { - IOUtil.close( writer ); - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TimeZone; + +import org.apache.maven.artifact.Artifact; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.WriterFactory; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.Xpp3DomBuilder; +import org.codehaus.plexus.util.xml.Xpp3DomUtils; +import org.codehaus.plexus.util.xml.Xpp3DomWriter; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Provides utility methods for artifact metadata processing. + * + * @author Benjamin Bentmann + */ +class MetadataUtils +{ + + /** + * Creates local metadata files for the specified artifact. The goal is to simulate the installation of the artifact + * by a local build, thereby decoupling the forked builds from the inderministic collection of remote repositories + * that are available to the main build and from which the artifact was originally resolved. + * + * @param file The artifact's file in the local test repository, must not be <code>null</code>. + * @param artifact The artifact to create metadata for, must not be <code>null</code>. + * @throws IOException If the metadata could not be created. + */ + public static void createMetadata( File file, Artifact artifact ) + throws IOException + { + TimeZone tz = java.util.TimeZone.getTimeZone( "UTC" ); + SimpleDateFormat fmt = new SimpleDateFormat( "yyyyMMddHHmmss" ); + fmt.setTimeZone( tz ); + String timestamp = fmt.format( new Date() ); + + if ( artifact.isSnapshot() ) + { + File metadataFile = new File( file.getParentFile(), "maven-metadata-local.xml" ); + + Xpp3Dom metadata = new Xpp3Dom( "metadata" ); + addChild( metadata, "groupId", artifact.getGroupId() ); + addChild( metadata, "artifactId", artifact.getArtifactId() ); + addChild( metadata, "version", artifact.getBaseVersion() ); + Xpp3Dom versioning = new Xpp3Dom( "versioning" ); + versioning.addChild( addChild( new Xpp3Dom( "snapshot" ), "localCopy", "true" ) ); + addChild( versioning, "lastUpdated", timestamp ); + metadata.addChild( versioning ); + + writeMetadata( metadataFile, metadata ); + } + + File metadataFile = new File( file.getParentFile().getParentFile(), "maven-metadata-local.xml" ); + + Set<String> allVersions = new LinkedHashSet<String>(); + + Xpp3Dom metadata = readMetadata( metadataFile ); + + if ( metadata != null ) + { + Xpp3Dom versioning = metadata.getChild( "versioning" ); + if ( versioning != null ) + { + Xpp3Dom versions = versioning.getChild( "versions" ); + if ( versions != null ) + { + + Xpp3Dom[] children = versions.getChildren( "version" ); + for ( Xpp3Dom aChildren : children ) + { + allVersions.add( aChildren.getValue() ); + } + } + } + } + + allVersions.add( artifact.getBaseVersion() ); + + metadata = new Xpp3Dom( "metadata" ); + addChild( metadata, "groupId", artifact.getGroupId() ); + addChild( metadata, "artifactId", artifact.getArtifactId() ); + Xpp3Dom versioning = new Xpp3Dom( "versioning" ); + versioning.addChild( addChildren( new Xpp3Dom( "versions" ), "version", allVersions ) ); + addChild( versioning, "lastUpdated", timestamp ); + metadata.addChild( versioning ); + + metadata = Xpp3DomUtils.mergeXpp3Dom( metadata, readMetadata( metadataFile ) ); + + writeMetadata( metadataFile, metadata ); + } + + private static Xpp3Dom addChild( Xpp3Dom parent, String childName, String childValue ) + { + Xpp3Dom child = new Xpp3Dom( childName ); + child.setValue( childValue ); + parent.addChild( child ); + return parent; + } + + private static Xpp3Dom addChildren( Xpp3Dom parent, String childName, Collection<String> childValues ) + { + for ( String childValue : childValues ) + { + addChild( parent, childName, childValue ); + } + return parent; + } + + private static Xpp3Dom readMetadata( File metadataFile ) + throws IOException + { + if ( !metadataFile.isFile() ) + { + return null; + } + + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( metadataFile ); + final Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( reader ); + reader.close(); + reader = null; + return xpp3Dom; + } + catch ( XmlPullParserException e ) + { + throw (IOException) new IOException( e.getMessage() ).initCause( e ); + } + finally + { + IOUtil.close( reader ); + } + } + + private static void writeMetadata( File metadataFile, Xpp3Dom metadata ) + throws IOException + { + metadataFile.getParentFile().mkdirs(); + + Writer writer = null; + try + { + writer = WriterFactory.newXmlWriter( metadataFile ); + Xpp3DomWriter.write( writer, metadata ); + writer.close(); + writer = null; + } + finally + { + IOUtil.close( writer ); + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/PomUtils.java b/src/main/java/org/apache/maven/plugins/invoker/PomUtils.java similarity index 97% rename from src/main/java/org/apache/maven/plugin/invoker/PomUtils.java rename to src/main/java/org/apache/maven/plugins/invoker/PomUtils.java index 8aba3e9..5130947 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/PomUtils.java +++ b/src/main/java/org/apache/maven/plugins/invoker/PomUtils.java
@@ -1,74 +1,74 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.IOException; -import java.io.Reader; - -import org.apache.maven.model.Model; -import org.apache.maven.model.io.xpp3.MavenXpp3Reader; -import org.apache.maven.plugin.MojoExecutionException; -import org.codehaus.plexus.util.IOUtil; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -/** - * Provides utility methods for POM processing. - * - * @author Benjamin Bentmann - */ -class PomUtils -{ - - /** - * Loads the (raw) model from the specified POM file. - * - * @param pomFile The path to the POM file to load, must not be <code>null</code>. - * @return The raw model, never <code>null</code>. - * @throws MojoExecutionException If the POM file could not be loaded. - */ - public static Model loadPom( File pomFile ) - throws MojoExecutionException - { - Reader reader = null; - try - { - reader = ReaderFactory.newXmlReader( pomFile ); - final Model model = new MavenXpp3Reader().read( reader, false ); - reader.close(); - reader = null; - return model; - } - catch ( XmlPullParserException e ) - { - throw new MojoExecutionException( "Failed to parse POM: " + pomFile, e ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to read POM: " + pomFile, e ); - } - finally - { - IOUtil.close( reader ); - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.IOException; +import java.io.Reader; + +import org.apache.maven.model.Model; +import org.apache.maven.model.io.xpp3.MavenXpp3Reader; +import org.apache.maven.plugin.MojoExecutionException; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Provides utility methods for POM processing. + * + * @author Benjamin Bentmann + */ +class PomUtils +{ + + /** + * Loads the (raw) model from the specified POM file. + * + * @param pomFile The path to the POM file to load, must not be <code>null</code>. + * @return The raw model, never <code>null</code>. + * @throws MojoExecutionException If the POM file could not be loaded. + */ + public static Model loadPom( File pomFile ) + throws MojoExecutionException + { + Reader reader = null; + try + { + reader = ReaderFactory.newXmlReader( pomFile ); + final Model model = new MavenXpp3Reader().read( reader, false ); + reader.close(); + reader = null; + return model; + } + catch ( XmlPullParserException e ) + { + throw new MojoExecutionException( "Failed to parse POM: " + pomFile, e ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to read POM: " + pomFile, e ); + } + finally + { + IOUtil.close( reader ); + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/ReportUtils.java b/src/main/java/org/apache/maven/plugins/invoker/ReportUtils.java similarity index 97% rename from src/main/java/org/apache/maven/plugin/invoker/ReportUtils.java rename to src/main/java/org/apache/maven/plugins/invoker/ReportUtils.java index 3a37072..9f4207c 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/ReportUtils.java +++ b/src/main/java/org/apache/maven/plugins/invoker/ReportUtils.java
@@ -1,63 +1,63 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.FilenameFilter; - -/** - * Provides utility methods for invoker report processing. - * - * @author Benjamin Bentmann - */ -class ReportUtils -{ - - private static class FileFilterOnlyXmlFile - implements FilenameFilter - { - - public boolean accept( File dir, String name ) - { - return name.startsWith( "BUILD-" ) && name.endsWith( ".xml" ); - } - - } - - /** - * Gets the paths to the invoker reports available in the specified directory. - * - * @param reportsDirectory The base directory where the invoker reports are located in, may be <code>null</code>. - * @return The paths to the invoker reports, can be empty but never <code>null</code>. - */ - public static File[] getReportFiles( File reportsDirectory ) - { - File[] reportFiles = - ( reportsDirectory != null ) ? reportsDirectory.listFiles( new FileFilterOnlyXmlFile() ) : null; - - if ( reportFiles == null ) - { - reportFiles = new File[0]; - } - - return reportFiles; - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.FilenameFilter; + +/** + * Provides utility methods for invoker report processing. + * + * @author Benjamin Bentmann + */ +class ReportUtils +{ + + private static class FileFilterOnlyXmlFile + implements FilenameFilter + { + + public boolean accept( File dir, String name ) + { + return name.startsWith( "BUILD-" ) && name.endsWith( ".xml" ); + } + + } + + /** + * Gets the paths to the invoker reports available in the specified directory. + * + * @param reportsDirectory The base directory where the invoker reports are located in, may be <code>null</code>. + * @return The paths to the invoker reports, can be empty but never <code>null</code>. + */ + public static File[] getReportFiles( File reportsDirectory ) + { + File[] reportFiles = + ( reportsDirectory != null ) ? reportsDirectory.listFiles( new FileFilterOnlyXmlFile() ) : null; + + if ( reportFiles == null ) + { + reportFiles = new File[0]; + } + + return reportFiles; + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/Selector.java b/src/main/java/org/apache/maven/plugins/invoker/Selector.java similarity index 98% rename from src/main/java/org/apache/maven/plugin/invoker/Selector.java rename to src/main/java/org/apache/maven/plugins/invoker/Selector.java index 102be42..d4fccc1 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/Selector.java +++ b/src/main/java/org/apache/maven/plugins/invoker/Selector.java
@@ -1,4 +1,4 @@ -package org.apache.maven.plugin.invoker; +package org.apache.maven.plugins.invoker; /* * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/src/main/java/org/apache/maven/plugin/invoker/SelectorUtils.java b/src/main/java/org/apache/maven/plugins/invoker/SelectorUtils.java similarity index 99% rename from src/main/java/org/apache/maven/plugin/invoker/SelectorUtils.java rename to src/main/java/org/apache/maven/plugins/invoker/SelectorUtils.java index d313280..5f18259 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/SelectorUtils.java +++ b/src/main/java/org/apache/maven/plugins/invoker/SelectorUtils.java
@@ -1,283 +1,283 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Properties; - -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.Os; -import org.codehaus.plexus.util.StringUtils; - -/** - * Provides utility methods for selecting build jobs based on environmental conditions. - * - * @author Benjamin Bentmann - */ -class SelectorUtils -{ - - static void parseList( String list, Collection<String> includes, Collection<String> excludes ) - { - String[] tokens = ( list != null ) ? StringUtils.split( list, "," ) : new String[0]; - - for ( String token1 : tokens ) - { - String token = token1.trim(); - - if ( token.startsWith( "!" ) ) - { - excludes.add( token.substring( 1 ) ); - } - else - { - includes.add( token ); - } - } - } - - static boolean isOsFamily( String osSpec ) - { - List<String> includes = new ArrayList<String>(); - List<String> excludes = new ArrayList<String>(); - parseList( osSpec, includes, excludes ); - - return isOsFamily( includes, true ) && !isOsFamily( excludes, false ); - } - - static boolean isOsFamily( List<String> families, boolean defaultMatch ) - { - if ( families != null && !families.isEmpty() ) - { - for ( String family : families ) - { - if ( Os.isFamily( family ) ) - { - return true; - } - } - - return false; - } - else - { - return defaultMatch; - } - } - - /** - * Retrieves the current Maven version. - * - * @return The current Maven version. - */ - static String getMavenVersion() - { - try - { - // This relies on the fact that MavenProject is the in core classloader - // and that the core classloader is for the maven-core artifact - // and that should have a pom.properties file - // if this ever changes, we will have to revisit this code. - Properties properties = new Properties(); - // CHECKSTYLE_OFF: LineLength - properties.load( MavenProject.class.getClassLoader().getResourceAsStream( "META-INF/maven/org.apache.maven/maven-core/pom.properties" ) ); - // CHECKSTYLE_ON: LineLength - return StringUtils.trim( properties.getProperty( "version" ) ); - } - catch ( Exception e ) - { - return null; - } - } - - static String getMavenVersion( File mavenHome ) - { - File mavenLib = new File( mavenHome, "lib" ); - File[] jarFiles = mavenLib.listFiles( new FilenameFilter() - { - public boolean accept( File dir, String name ) - { - return name.endsWith( ".jar" ); - } - } ); - - for ( File file : jarFiles ) - { - try - { - @SuppressWarnings( "deprecation" ) - URL url = - new URL( "jar:" + file.toURL().toExternalForm() - + "!/META-INF/maven/org.apache.maven/maven-core/pom.properties" ); - - Properties properties = new Properties(); - properties.load( url.openStream() ); - String version = StringUtils.trim( properties.getProperty( "version" ) ); - if ( version != null ) - { - return version; - } - } - catch ( MalformedURLException e ) - { - // ignore - } - catch ( IOException e ) - { - // ignore - } - } - return null; - } - - static boolean isMavenVersion( String mavenSpec ) - { - return isMavenVersion( mavenSpec, getMavenVersion() ); - } - - static boolean isMavenVersion( String mavenSpec, String actualVersion ) - { - List<String> includes = new ArrayList<String>(); - List<String> excludes = new ArrayList<String>(); - parseList( mavenSpec, includes, excludes ); - - List<Integer> mavenVersionList = parseVersion( actualVersion ); - - return isJreVersion( mavenVersionList, includes, true ) && !isJreVersion( mavenVersionList, excludes, false ); - } - - static String getJreVersion() - { - return System.getProperty( "java.version", "" ); - } - - static String getJreVersion( File javaHome ) - { - // @todo detect actual version - return null; - } - - static boolean isJreVersion( String jreSpec ) - { - return isJreVersion( jreSpec, getJreVersion() ); - } - - static boolean isJreVersion( String jreSpec, String actualJreVersion ) - { - List<String> includes = new ArrayList<String>(); - List<String> excludes = new ArrayList<String>(); - parseList( jreSpec, includes, excludes ); - - List<Integer> jreVersion = parseVersion( actualJreVersion ); - - return isJreVersion( jreVersion, includes, true ) && !isJreVersion( jreVersion, excludes, false ); - } - - static boolean isJreVersion( List<Integer> jreVersion, List<String> versionPatterns, boolean defaultMatch ) - { - if ( versionPatterns != null && !versionPatterns.isEmpty() ) - { - for ( String versionPattern : versionPatterns ) - { - if ( isJreVersion( jreVersion, versionPattern ) ) - { - return true; - } - } - - return false; - } - else - { - return defaultMatch; - } - } - - static boolean isJreVersion( List<Integer> jreVersion, String versionPattern ) - { - List<Integer> checkVersion = parseVersion( versionPattern ); - - if ( versionPattern.endsWith( "+" ) ) - { - // 1.5+ <=> [1.5,) - return compareVersions( jreVersion, checkVersion ) >= 0; - } - else if ( versionPattern.endsWith( "-" ) ) - { - // 1.5- <=> (,1.5) - return compareVersions( jreVersion, checkVersion ) < 0; - } - else - { - // 1.5 <=> [1.5,1.6) - return checkVersion.size() <= jreVersion.size() - && checkVersion.equals( jreVersion.subList( 0, checkVersion.size() ) ); - } - } - - static List<Integer> parseVersion( String version ) - { - version = version.replaceAll( "[^0-9]", "." ); - - String[] tokens = StringUtils.split( version, "." ); - - List<Integer> numbers = new ArrayList<Integer>(); - - for ( String token : tokens ) - { - numbers.add( Integer.valueOf( token ) ); - } - - return numbers; - } - - static int compareVersions( List<Integer> version1, List<Integer> version2 ) - { - for ( Iterator<Integer> it1 = version1.iterator(), it2 = version2.iterator();; ) - { - if ( !it1.hasNext() ) - { - return it2.hasNext() ? -1 : 0; - } - if ( !it2.hasNext() ) - { - return it1.hasNext() ? 1 : 0; - } - - Integer num1 = it1.next(); - Integer num2 = it2.next(); - - int rel = num1.compareTo( num2 ); - if ( rel != 0 ) - { - return rel; - } - } - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; + +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.util.Os; +import org.codehaus.plexus.util.StringUtils; + +/** + * Provides utility methods for selecting build jobs based on environmental conditions. + * + * @author Benjamin Bentmann + */ +class SelectorUtils +{ + + static void parseList( String list, Collection<String> includes, Collection<String> excludes ) + { + String[] tokens = ( list != null ) ? StringUtils.split( list, "," ) : new String[0]; + + for ( String token1 : tokens ) + { + String token = token1.trim(); + + if ( token.startsWith( "!" ) ) + { + excludes.add( token.substring( 1 ) ); + } + else + { + includes.add( token ); + } + } + } + + static boolean isOsFamily( String osSpec ) + { + List<String> includes = new ArrayList<String>(); + List<String> excludes = new ArrayList<String>(); + parseList( osSpec, includes, excludes ); + + return isOsFamily( includes, true ) && !isOsFamily( excludes, false ); + } + + static boolean isOsFamily( List<String> families, boolean defaultMatch ) + { + if ( families != null && !families.isEmpty() ) + { + for ( String family : families ) + { + if ( Os.isFamily( family ) ) + { + return true; + } + } + + return false; + } + else + { + return defaultMatch; + } + } + + /** + * Retrieves the current Maven version. + * + * @return The current Maven version. + */ + static String getMavenVersion() + { + try + { + // This relies on the fact that MavenProject is the in core classloader + // and that the core classloader is for the maven-core artifact + // and that should have a pom.properties file + // if this ever changes, we will have to revisit this code. + Properties properties = new Properties(); + // CHECKSTYLE_OFF: LineLength + properties.load( MavenProject.class.getClassLoader().getResourceAsStream( "META-INF/maven/org.apache.maven/maven-core/pom.properties" ) ); + // CHECKSTYLE_ON: LineLength + return StringUtils.trim( properties.getProperty( "version" ) ); + } + catch ( Exception e ) + { + return null; + } + } + + static String getMavenVersion( File mavenHome ) + { + File mavenLib = new File( mavenHome, "lib" ); + File[] jarFiles = mavenLib.listFiles( new FilenameFilter() + { + public boolean accept( File dir, String name ) + { + return name.endsWith( ".jar" ); + } + } ); + + for ( File file : jarFiles ) + { + try + { + @SuppressWarnings( "deprecation" ) + URL url = + new URL( "jar:" + file.toURL().toExternalForm() + + "!/META-INF/maven/org.apache.maven/maven-core/pom.properties" ); + + Properties properties = new Properties(); + properties.load( url.openStream() ); + String version = StringUtils.trim( properties.getProperty( "version" ) ); + if ( version != null ) + { + return version; + } + } + catch ( MalformedURLException e ) + { + // ignore + } + catch ( IOException e ) + { + // ignore + } + } + return null; + } + + static boolean isMavenVersion( String mavenSpec ) + { + return isMavenVersion( mavenSpec, getMavenVersion() ); + } + + static boolean isMavenVersion( String mavenSpec, String actualVersion ) + { + List<String> includes = new ArrayList<String>(); + List<String> excludes = new ArrayList<String>(); + parseList( mavenSpec, includes, excludes ); + + List<Integer> mavenVersionList = parseVersion( actualVersion ); + + return isJreVersion( mavenVersionList, includes, true ) && !isJreVersion( mavenVersionList, excludes, false ); + } + + static String getJreVersion() + { + return System.getProperty( "java.version", "" ); + } + + static String getJreVersion( File javaHome ) + { + // @todo detect actual version + return null; + } + + static boolean isJreVersion( String jreSpec ) + { + return isJreVersion( jreSpec, getJreVersion() ); + } + + static boolean isJreVersion( String jreSpec, String actualJreVersion ) + { + List<String> includes = new ArrayList<String>(); + List<String> excludes = new ArrayList<String>(); + parseList( jreSpec, includes, excludes ); + + List<Integer> jreVersion = parseVersion( actualJreVersion ); + + return isJreVersion( jreVersion, includes, true ) && !isJreVersion( jreVersion, excludes, false ); + } + + static boolean isJreVersion( List<Integer> jreVersion, List<String> versionPatterns, boolean defaultMatch ) + { + if ( versionPatterns != null && !versionPatterns.isEmpty() ) + { + for ( String versionPattern : versionPatterns ) + { + if ( isJreVersion( jreVersion, versionPattern ) ) + { + return true; + } + } + + return false; + } + else + { + return defaultMatch; + } + } + + static boolean isJreVersion( List<Integer> jreVersion, String versionPattern ) + { + List<Integer> checkVersion = parseVersion( versionPattern ); + + if ( versionPattern.endsWith( "+" ) ) + { + // 1.5+ <=> [1.5,) + return compareVersions( jreVersion, checkVersion ) >= 0; + } + else if ( versionPattern.endsWith( "-" ) ) + { + // 1.5- <=> (,1.5) + return compareVersions( jreVersion, checkVersion ) < 0; + } + else + { + // 1.5 <=> [1.5,1.6) + return checkVersion.size() <= jreVersion.size() + && checkVersion.equals( jreVersion.subList( 0, checkVersion.size() ) ); + } + } + + static List<Integer> parseVersion( String version ) + { + version = version.replaceAll( "[^0-9]", "." ); + + String[] tokens = StringUtils.split( version, "." ); + + List<Integer> numbers = new ArrayList<Integer>(); + + for ( String token : tokens ) + { + numbers.add( Integer.valueOf( token ) ); + } + + return numbers; + } + + static int compareVersions( List<Integer> version1, List<Integer> version2 ) + { + for ( Iterator<Integer> it1 = version1.iterator(), it2 = version2.iterator();; ) + { + if ( !it1.hasNext() ) + { + return it2.hasNext() ? -1 : 0; + } + if ( !it2.hasNext() ) + { + return it1.hasNext() ? 1 : 0; + } + + Integer num1 = it1.next(); + Integer num2 = it2.next(); + + int rel = num1.compareTo( num2 ); + if ( rel != 0 ) + { + return rel; + } + } + } + +}
diff --git a/src/main/java/org/apache/maven/plugin/invoker/SystemPropertyPrinter.java b/src/main/java/org/apache/maven/plugins/invoker/SystemPropertyPrinter.java similarity index 96% rename from src/main/java/org/apache/maven/plugin/invoker/SystemPropertyPrinter.java rename to src/main/java/org/apache/maven/plugins/invoker/SystemPropertyPrinter.java index 6441e82..9c28b1d 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/SystemPropertyPrinter.java +++ b/src/main/java/org/apache/maven/plugins/invoker/SystemPropertyPrinter.java
@@ -1,4 +1,4 @@ -package org.apache.maven.plugin.invoker; +package org.apache.maven.plugins.invoker; /* * Licensed to the Apache Software Foundation (ASF) under one
diff --git a/src/main/java/org/apache/maven/plugin/invoker/VerifyMojo.java b/src/main/java/org/apache/maven/plugins/invoker/VerifyMojo.java similarity index 97% rename from src/main/java/org/apache/maven/plugin/invoker/VerifyMojo.java rename to src/main/java/org/apache/maven/plugins/invoker/VerifyMojo.java index 5078cd4..6feb0a4 100644 --- a/src/main/java/org/apache/maven/plugin/invoker/VerifyMojo.java +++ b/src/main/java/org/apache/maven/plugins/invoker/VerifyMojo.java
@@ -1,140 +1,140 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.invoker.model.io.xpp3.BuildJobXpp3Reader; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.codehaus.plexus.util.ReaderFactory; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -import java.io.File; -import java.io.IOException; - -/** - * Checks the results of maven-invoker-plugin based integration tests and fails the build if any tests failed. - * - * @author Olivier Lamy - * @since 1.4 - */ -@Mojo( name = "verify", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true ) -public class VerifyMojo - extends AbstractMojo -{ - - /** - * Flag used to suppress certain invocations. This is useful in tailoring the build using profiles. - * - * @since 1.1 - */ - @Parameter( property = "invoker.skip", defaultValue = "false" ) - private boolean skipInvocation; - - /** - * Base directory where all build reports are read from. - * - * @since 1.4 - */ - @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" ) - private File reportsDirectory; - - /** - * A flag controlling whether failures of the sub builds should fail the main build, too. If set to - * <code>true</code>, the main build will proceed even if one or more sub builds failed. - * - * @since 1.3 - */ - @Parameter( property = "maven.test.failure.ignore", defaultValue = "false" ) - private boolean ignoreFailures; - - /** - * Flag used to suppress the summary output notifying of successes and failures. If set to <code>true</code>, the - * only indication of the build's success or failure will be the effect it has on the main build (if it fails, the - * main build should fail as well). - */ - @Parameter( defaultValue = "false" ) - private boolean suppressSummaries; - - /** - * Set this to <code>true</code> to cause a failure if there are no projects to invoke. - * - * @since 1.9 - */ - @Parameter( property = "invoker.failIfNoProjects" ) - private Boolean failIfNoProjects; - - /** - * Invokes Maven on the configured test projects. - * - * @throws org.apache.maven.plugin.MojoExecutionException If the goal encountered severe errors. - * @throws org.apache.maven.plugin.MojoFailureException If any of the Maven builds failed. - */ - public void execute() - throws MojoExecutionException, MojoFailureException - { - if ( skipInvocation ) - { - getLog().info( "Skipping invocation per configuration." - + " If this is incorrect, ensure the skipInvocation parameter is not set to true." ); - return; - } - - File[] reportFiles = ReportUtils.getReportFiles( reportsDirectory ); - if ( reportFiles.length <= 0 ) - { - if ( Boolean.TRUE.equals( failIfNoProjects ) ) - { - throw new MojoFailureException( "No projects to invoke!" ); - } - getLog().info( "No invoker report files found, nothing to check." ); - return; - } - - InvokerSession invokerSession = new InvokerSession(); - for ( File reportFile : reportFiles ) - { - try - { - BuildJobXpp3Reader reader = new BuildJobXpp3Reader(); - invokerSession.addJob( reader.read( ReaderFactory.newXmlReader( reportFile ) ) ); - } - catch ( XmlPullParserException e ) - { - throw new MojoExecutionException( "Failed to parse report file: " + reportFile, e ); - } - catch ( IOException e ) - { - throw new MojoExecutionException( "Failed to read report file: " + reportFile, e ); - } - } - - if ( !suppressSummaries ) - { - invokerSession.logSummary( getLog(), ignoreFailures ); - } - - invokerSession.handleFailures( getLog(), ignoreFailures ); - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.invoker.model.io.xpp3.BuildJobXpp3Reader; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.util.ReaderFactory; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +import java.io.File; +import java.io.IOException; + +/** + * Checks the results of maven-invoker-plugin based integration tests and fails the build if any tests failed. + * + * @author Olivier Lamy + * @since 1.4 + */ +@Mojo( name = "verify", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true ) +public class VerifyMojo + extends AbstractMojo +{ + + /** + * Flag used to suppress certain invocations. This is useful in tailoring the build using profiles. + * + * @since 1.1 + */ + @Parameter( property = "invoker.skip", defaultValue = "false" ) + private boolean skipInvocation; + + /** + * Base directory where all build reports are read from. + * + * @since 1.4 + */ + @Parameter( property = "invoker.reportsDirectory", defaultValue = "${project.build.directory}/invoker-reports" ) + private File reportsDirectory; + + /** + * A flag controlling whether failures of the sub builds should fail the main build, too. If set to + * <code>true</code>, the main build will proceed even if one or more sub builds failed. + * + * @since 1.3 + */ + @Parameter( property = "maven.test.failure.ignore", defaultValue = "false" ) + private boolean ignoreFailures; + + /** + * Flag used to suppress the summary output notifying of successes and failures. If set to <code>true</code>, the + * only indication of the build's success or failure will be the effect it has on the main build (if it fails, the + * main build should fail as well). + */ + @Parameter( defaultValue = "false" ) + private boolean suppressSummaries; + + /** + * Set this to <code>true</code> to cause a failure if there are no projects to invoke. + * + * @since 1.9 + */ + @Parameter( property = "invoker.failIfNoProjects" ) + private Boolean failIfNoProjects; + + /** + * Invokes Maven on the configured test projects. + * + * @throws org.apache.maven.plugin.MojoExecutionException If the goal encountered severe errors. + * @throws org.apache.maven.plugin.MojoFailureException If any of the Maven builds failed. + */ + public void execute() + throws MojoExecutionException, MojoFailureException + { + if ( skipInvocation ) + { + getLog().info( "Skipping invocation per configuration." + + " If this is incorrect, ensure the skipInvocation parameter is not set to true." ); + return; + } + + File[] reportFiles = ReportUtils.getReportFiles( reportsDirectory ); + if ( reportFiles.length <= 0 ) + { + if ( Boolean.TRUE.equals( failIfNoProjects ) ) + { + throw new MojoFailureException( "No projects to invoke!" ); + } + getLog().info( "No invoker report files found, nothing to check." ); + return; + } + + InvokerSession invokerSession = new InvokerSession(); + for ( File reportFile : reportFiles ) + { + try + { + BuildJobXpp3Reader reader = new BuildJobXpp3Reader(); + invokerSession.addJob( reader.read( ReaderFactory.newXmlReader( reportFile ) ) ); + } + catch ( XmlPullParserException e ) + { + throw new MojoExecutionException( "Failed to parse report file: " + reportFile, e ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Failed to read report file: " + reportFile, e ); + } + } + + if ( !suppressSummaries ) + { + invokerSession.logSummary( getLog(), ignoreFailures ); + } + + invokerSession.handleFailures( getLog(), ignoreFailures ); + } + +}
diff --git a/src/main/mdo/invocation.mdo b/src/main/mdo/invocation.mdo index 12505d3..f7016cd 100644 --- a/src/main/mdo/invocation.mdo +++ b/src/main/mdo/invocation.mdo
@@ -32,7 +32,7 @@ <defaults> <default> <key>package</key> - <value>org.apache.maven.plugin.invoker.model</value> + <value>org.apache.maven.plugins.invoker.model</value> </default> </defaults> <versionDefinition>
diff --git a/src/test/java/org/apache/maven/plugin/invoker/ExtendedMavenProjectStub.java b/src/test/java/org/apache/maven/plugins/invoker/ExtendedMavenProjectStub.java similarity index 91% rename from src/test/java/org/apache/maven/plugin/invoker/ExtendedMavenProjectStub.java rename to src/test/java/org/apache/maven/plugins/invoker/ExtendedMavenProjectStub.java index 55e71a1..ae9c311 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/ExtendedMavenProjectStub.java +++ b/src/test/java/org/apache/maven/plugins/invoker/ExtendedMavenProjectStub.java
@@ -1,57 +1,57 @@ -/* - * 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 org.apache.maven.plugin.invoker; - -import org.apache.maven.model.Scm; -import org.apache.maven.plugin.testing.stubs.MavenProjectStub; - -import java.util.Properties; - -/** - * @author Olivier Lamy - * @since 22 nov. 07 - * @version $Id$ - */ -public class ExtendedMavenProjectStub - extends MavenProjectStub -{ - private Scm scm; - - private Properties properties; - - public Scm getScm() - { - return scm; - } - - public void setScm( Scm scm ) - { - this.scm = scm; - } - - public Properties getProperties() - { - return properties; - } - - public void setProperties( Properties properties ) - { - this.properties = properties; - } -} +/* + * 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 org.apache.maven.plugins.invoker; + +import org.apache.maven.model.Scm; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; + +import java.util.Properties; + +/** + * @author Olivier Lamy + * @since 22 nov. 07 + * @version $Id: ExtendedMavenProjectStub.java 1606207 2014-06-27 20:27:03Z khmarbaise $ + */ +public class ExtendedMavenProjectStub + extends MavenProjectStub +{ + private Scm scm; + + private Properties properties; + + public Scm getScm() + { + return scm; + } + + public void setScm( Scm scm ) + { + this.scm = scm; + } + + public Properties getProperties() + { + return properties; + } + + public void setProperties( Properties properties ) + { + this.properties = properties; + } +}
diff --git a/src/test/java/org/apache/maven/plugin/invoker/InterpolationTest.java b/src/test/java/org/apache/maven/plugins/invoker/InterpolationTest.java similarity index 95% rename from src/test/java/org/apache/maven/plugin/invoker/InterpolationTest.java rename to src/test/java/org/apache/maven/plugins/invoker/InterpolationTest.java index 2a45dda..a63ac9e 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/InterpolationTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/InterpolationTest.java
@@ -1,126 +1,129 @@ -/* - * 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 org.apache.maven.plugin.invoker; - -import java.io.File; -import java.io.Reader; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.apache.maven.model.Scm; -import org.apache.maven.plugin.testing.AbstractMojoTestCase; -import org.apache.maven.plugin.testing.stubs.MavenProjectStub; -import org.apache.maven.settings.Settings; -import org.codehaus.plexus.util.IOUtil; -import org.codehaus.plexus.util.ReaderFactory; - -/** - * @author Olivier Lamy - * @since 22 nov. 07 - * @version $Id$ - */ -public class InterpolationTest - extends AbstractMojoTestCase -{ - - protected MavenProjectStub buildMavenProjectStub() - { - ExtendedMavenProjectStub project = new ExtendedMavenProjectStub(); - project.setVersion( "1.0-SNAPSHOT" ); - project.setArtifactId( "foo" ); - project.setGroupId( "bar" ); - Properties properties = new Properties(); - properties.put( "fooOnProject", "barOnProject" ); - project.setProperties( properties ); - Scm scm = new Scm(); - scm.setConnection( "http://blabla" ); - project.setScm( scm ); - return project; - } - - public void testCompositeMap() - throws Exception - { - - Properties properties = new Properties(); - properties.put( "foo", "bar" ); - properties.put( "version", "2.0-SNAPSHOT" ); - CompositeMap compositeMap = new CompositeMap( buildMavenProjectStub(), (Map) properties, false ); - assertEquals( "1.0-SNAPSHOT", compositeMap.get( "pom.version" ) ); - assertEquals( "bar", compositeMap.get( "foo" ) ); - assertEquals( "bar", compositeMap.get( "pom.groupId" ) ); - assertEquals( "http://blabla", compositeMap.get( "pom.scm.connection" ) ); - assertEquals( "barOnProject", compositeMap.get( "fooOnProject" ) ); - } - - public void testPomInterpolation() - throws Exception - { - Reader reader = null; - File interpolatedPomFile; - try - { - InvokerMojo invokerMojo = new InvokerMojo(); - setVariableValueToObject( invokerMojo, "project", buildMavenProjectStub() ); - setVariableValueToObject( invokerMojo, "settings", new Settings() ); - Properties properties = new Properties(); - properties.put( "foo", "bar" ); - properties.put( "version", "2.0-SNAPSHOT" ); - setVariableValueToObject( invokerMojo, "filterProperties", properties ); - String dirPath = getBasedir() + File.separatorChar + "src" + File.separatorChar + "test" - + File.separatorChar + "resources" + File.separatorChar + "unit" + File.separatorChar + "interpolation"; - - interpolatedPomFile = new File( getBasedir(), "target/interpolated-pom.xml" ); - invokerMojo.buildInterpolatedFile( new File( dirPath, "pom.xml" ), interpolatedPomFile ); - reader = ReaderFactory.newXmlReader( interpolatedPomFile ); - String content = IOUtil.toString( reader ); - assertTrue( content.indexOf( "<interpolateValue>bar</interpolateValue>" ) > 0 ); - reader.close(); - reader = null; - // recreate it to test delete if exists before creation - invokerMojo.buildInterpolatedFile( new File( dirPath, "pom.xml" ), interpolatedPomFile ); - reader = ReaderFactory.newXmlReader( interpolatedPomFile ); - content = IOUtil.toString( reader ); - assertTrue( content.indexOf( "<interpolateValue>bar</interpolateValue>" ) > 0 ); - reader.close(); - reader = null; - } - finally - { - IOUtil.close( reader ); - } - } - - public void testProfilesWithNoFile() - throws Exception - { - - InvokerMojo invokerMojo = new InvokerMojo(); - setVariableValueToObject( invokerMojo, "profiles", Arrays.asList( "zloug" ) ); - setVariableValueToObject( invokerMojo, "settings", new Settings() ); - String dirPath = getBasedir() + File.separatorChar + "src" + File.separatorChar + "test" + File.separatorChar - + "resources" + File.separatorChar + "unit" + File.separatorChar + "profiles-from-file"; - List<String> profiles = invokerMojo.getProfiles( new File( dirPath ) ); - assertTrue( profiles.contains( "zloug" ) ); - assertEquals( 1, profiles.size() ); - - } -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.io.Reader; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.maven.model.Scm; +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.plugin.testing.stubs.MavenProjectStub; +import org.apache.maven.plugins.invoker.CompositeMap; +import org.apache.maven.plugins.invoker.InvokerMojo; +import org.apache.maven.settings.Settings; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.ReaderFactory; + +/** + * @author Olivier Lamy + * @since 22 nov. 07 + * @version $Id: InterpolationTest.java 1783819 2017-02-21 02:30:23Z schulte $ + */ +public class InterpolationTest + extends AbstractMojoTestCase +{ + + protected MavenProjectStub buildMavenProjectStub() + { + ExtendedMavenProjectStub project = new ExtendedMavenProjectStub(); + project.setVersion( "1.0-SNAPSHOT" ); + project.setArtifactId( "foo" ); + project.setGroupId( "bar" ); + Properties properties = new Properties(); + properties.put( "fooOnProject", "barOnProject" ); + project.setProperties( properties ); + Scm scm = new Scm(); + scm.setConnection( "http://blabla" ); + project.setScm( scm ); + return project; + } + + public void testCompositeMap() + throws Exception + { + + Properties properties = new Properties(); + properties.put( "foo", "bar" ); + properties.put( "version", "2.0-SNAPSHOT" ); + CompositeMap compositeMap = new CompositeMap( buildMavenProjectStub(), (Map) properties, false ); + assertEquals( "1.0-SNAPSHOT", compositeMap.get( "pom.version" ) ); + assertEquals( "bar", compositeMap.get( "foo" ) ); + assertEquals( "bar", compositeMap.get( "pom.groupId" ) ); + assertEquals( "http://blabla", compositeMap.get( "pom.scm.connection" ) ); + assertEquals( "barOnProject", compositeMap.get( "fooOnProject" ) ); + } + + public void testPomInterpolation() + throws Exception + { + Reader reader = null; + File interpolatedPomFile; + try + { + InvokerMojo invokerMojo = new InvokerMojo(); + setVariableValueToObject( invokerMojo, "project", buildMavenProjectStub() ); + setVariableValueToObject( invokerMojo, "settings", new Settings() ); + Properties properties = new Properties(); + properties.put( "foo", "bar" ); + properties.put( "version", "2.0-SNAPSHOT" ); + setVariableValueToObject( invokerMojo, "filterProperties", properties ); + String dirPath = getBasedir() + File.separatorChar + "src" + File.separatorChar + "test" + + File.separatorChar + "resources" + File.separatorChar + "unit" + File.separatorChar + "interpolation"; + + interpolatedPomFile = new File( getBasedir(), "target/interpolated-pom.xml" ); + invokerMojo.buildInterpolatedFile( new File( dirPath, "pom.xml" ), interpolatedPomFile ); + reader = ReaderFactory.newXmlReader( interpolatedPomFile ); + String content = IOUtil.toString( reader ); + assertTrue( content.indexOf( "<interpolateValue>bar</interpolateValue>" ) > 0 ); + reader.close(); + reader = null; + // recreate it to test delete if exists before creation + invokerMojo.buildInterpolatedFile( new File( dirPath, "pom.xml" ), interpolatedPomFile ); + reader = ReaderFactory.newXmlReader( interpolatedPomFile ); + content = IOUtil.toString( reader ); + assertTrue( content.indexOf( "<interpolateValue>bar</interpolateValue>" ) > 0 ); + reader.close(); + reader = null; + } + finally + { + IOUtil.close( reader ); + } + } + + public void testProfilesWithNoFile() + throws Exception + { + + InvokerMojo invokerMojo = new InvokerMojo(); + setVariableValueToObject( invokerMojo, "profiles", Arrays.asList( "zloug" ) ); + setVariableValueToObject( invokerMojo, "settings", new Settings() ); + String dirPath = getBasedir() + File.separatorChar + "src" + File.separatorChar + "test" + File.separatorChar + + "resources" + File.separatorChar + "unit" + File.separatorChar + "profiles-from-file"; + List<String> profiles = invokerMojo.getProfiles( new File( dirPath ) ); + assertTrue( profiles.contains( "zloug" ) ); + assertEquals( 1, profiles.size() ); + + } +}
diff --git a/src/test/java/org/apache/maven/plugin/invoker/InvokerMojoTest.java b/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java similarity index 94% rename from src/test/java/org/apache/maven/plugin/invoker/InvokerMojoTest.java rename to src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java index fc2df8f..68edc64 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/InvokerMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/InvokerMojoTest.java
@@ -1,87 +1,87 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.util.Collections; -import java.util.List; - -import org.apache.maven.plugin.invoker.model.BuildJob; -import org.apache.maven.plugin.testing.AbstractMojoTestCase; - -/** - * @author Olivier Lamy - * @since 18 nov. 07 - * @version $Id$ - */ -public class InvokerMojoTest - extends AbstractMojoTestCase -{ - - public void testSingleInvokerTest() - throws Exception - { - InvokerMojo invokerMojo = new InvokerMojo(); - String dirPath = getBasedir() + "/src/test/resources/unit"; - List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); - assertEquals( 1, goals.size() ); - setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); - setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 1, poms.length ); - } - - public void testMultiInvokerTest() - throws Exception - { - InvokerMojo invokerMojo = new InvokerMojo(); - String dirPath = getBasedir() + "/src/test/resources/unit"; - List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); - assertEquals( 1, goals.size() ); - setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); - setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*,*terpolatio*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 2, poms.length ); - } - - public void testFullPatternInvokerTest() - throws Exception - { - InvokerMojo invokerMojo = new InvokerMojo(); - String dirPath = getBasedir() + "/src/test/resources/unit"; - List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); - assertEquals( 1, goals.size() ); - setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); - setVariableValueToObject( invokerMojo, "invokerTest", "*" ); - BuildJob[] poms = invokerMojo.getBuildJobs(); - assertEquals( 4, poms.length ); - } - - public void testAlreadyCloned() - throws Exception - { - assertFalse( AbstractInvokerMojo.alreadyCloned( "dir", Collections.<String>emptyList() ) ); - assertTrue( AbstractInvokerMojo.alreadyCloned( "dir", Collections.singletonList( "dir" ) ) ); - assertTrue( AbstractInvokerMojo.alreadyCloned( "dir" + File.separator + "sub", - Collections.singletonList( "dir" ) ) ); - assertFalse( AbstractInvokerMojo.alreadyCloned( "dirs", Collections.singletonList( "dir" ) ) ); - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.plugin.testing.AbstractMojoTestCase; +import org.apache.maven.plugins.invoker.model.BuildJob; + +/** + * @author Olivier Lamy + * @since 18 nov. 07 + * @version $Id: InvokerMojoTest.java 1731726 2016-02-22 19:34:37Z khmarbaise $ + */ +public class InvokerMojoTest + extends AbstractMojoTestCase +{ + + public void testSingleInvokerTest() + throws Exception + { + InvokerMojo invokerMojo = new InvokerMojo(); + String dirPath = getBasedir() + "/src/test/resources/unit"; + List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); + assertEquals( 1, goals.size() ); + setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*" ); + BuildJob[] poms = invokerMojo.getBuildJobs(); + assertEquals( 1, poms.length ); + } + + public void testMultiInvokerTest() + throws Exception + { + InvokerMojo invokerMojo = new InvokerMojo(); + String dirPath = getBasedir() + "/src/test/resources/unit"; + List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); + assertEquals( 1, goals.size() ); + setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerTest", "*dummy*,*terpolatio*" ); + BuildJob[] poms = invokerMojo.getBuildJobs(); + assertEquals( 2, poms.length ); + } + + public void testFullPatternInvokerTest() + throws Exception + { + InvokerMojo invokerMojo = new InvokerMojo(); + String dirPath = getBasedir() + "/src/test/resources/unit"; + List<String> goals = invokerMojo.getGoals( new File( dirPath ) ); + assertEquals( 1, goals.size() ); + setVariableValueToObject( invokerMojo, "projectsDirectory", new File( dirPath ) ); + setVariableValueToObject( invokerMojo, "invokerTest", "*" ); + BuildJob[] poms = invokerMojo.getBuildJobs(); + assertEquals( 4, poms.length ); + } + + public void testAlreadyCloned() + throws Exception + { + assertFalse( AbstractInvokerMojo.alreadyCloned( "dir", Collections.<String>emptyList() ) ); + assertTrue( AbstractInvokerMojo.alreadyCloned( "dir", Collections.singletonList( "dir" ) ) ); + assertTrue( AbstractInvokerMojo.alreadyCloned( "dir" + File.separator + "sub", + Collections.singletonList( "dir" ) ) ); + assertFalse( AbstractInvokerMojo.alreadyCloned( "dirs", Collections.singletonList( "dir" ) ) ); + } + +}
diff --git a/src/test/java/org/apache/maven/plugin/invoker/InvokerPropertiesTest.java b/src/test/java/org/apache/maven/plugins/invoker/InvokerPropertiesTest.java similarity index 98% rename from src/test/java/org/apache/maven/plugin/invoker/InvokerPropertiesTest.java rename to src/test/java/org/apache/maven/plugins/invoker/InvokerPropertiesTest.java index 015f199..6d11613 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/InvokerPropertiesTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/InvokerPropertiesTest.java
@@ -1,294 +1,295 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.Properties; - -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.InvocationRequest; - -import junit.framework.TestCase; - -/** - * Tests the invoker properties facade. - * - * @author Benjamin Bentmann - * @version $Id$ - */ -public class InvokerPropertiesTest - extends TestCase -{ - - public void testConstructorNullSafe() - throws Exception - { - InvokerProperties facade = new InvokerProperties( null ); - assertNotNull( facade.getProperties() ); - } - - public void testGetInvokerProperty() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - assertNull( facade.get( "undefined-key", 0 ) ); - - props.setProperty( "key", "value" ); - assertEquals( "value", facade.get( "key", 1 ) ); - - props.setProperty( "key.1", "another-value" ); - assertEquals( "another-value", facade.get( "key", 1 ) ); - assertEquals( "value", facade.get( "key", 2 ) ); - } - - public void testGetJobName() - throws Exception - { - Properties props = new Properties(); - final String jobName = "Build Job name"; - props.put( "invoker.name", jobName ); - InvokerProperties facade = new InvokerProperties( props ); - - assertEquals( jobName, facade.getJobName() ); - } - - public void testIsExpectedResult() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - assertTrue( facade.isExpectedResult( 0, 0 ) ); - assertFalse( facade.isExpectedResult( 1, 0 ) ); - - props.setProperty( "invoker.buildResult", "success" ); - assertTrue( facade.isExpectedResult( 0, 0 ) ); - assertFalse( facade.isExpectedResult( 1, 0 ) ); - - props.setProperty( "invoker.buildResult", "failure" ); - assertFalse( facade.isExpectedResult( 0, 0 ) ); - assertTrue( facade.isExpectedResult( 1, 0 ) ); - } - - public void testConfigureRequestGoals() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setGoals( Collections.singletonList( "test" ) ); - facade.configureInvocation( request, 0 ); - assertEquals( Collections.singletonList( "test" ), request.getGoals() ); - - props.setProperty( "invoker.goals", "verify" ); - facade.configureInvocation( request, 0 ); - assertEquals( Collections.singletonList( "verify" ), request.getGoals() ); - - props.setProperty( "invoker.goals", " " ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[0] ), request.getGoals() ); - - props.setProperty( "invoker.goals", " clean , test verify " ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[] { "clean", "test", "verify" } ), request.getGoals() ); - - props.setProperty( "invoker.goals", "" ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[0] ), request.getGoals() ); - } - - public void testConfigureRequestProfiles() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setProfiles( Collections.singletonList( "test" ) ); - facade.configureInvocation( request, 0 ); - assertEquals( Collections.singletonList( "test" ), request.getProfiles() ); - - props.setProperty( "invoker.profiles", "verify" ); - facade.configureInvocation( request, 0 ); - assertEquals( Collections.singletonList( "verify" ), request.getProfiles() ); - - props.setProperty( "invoker.profiles", " " ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[0] ), request.getProfiles() ); - - props.setProperty( "invoker.profiles", " clean , test verify ," ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[] { "clean", "test", "verify" } ), request.getProfiles() ); - - props.setProperty( "invoker.profiles", "" ); - facade.configureInvocation( request, 0 ); - assertEquals( Arrays.asList( new String[0] ), request.getProfiles() ); - } - - public void testConfigureRequestProject() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - File tempPom = File.createTempFile( "maven-invoker-plugin-test", ".pom" ); - File tempDir = tempPom.getParentFile(); - - request.setBaseDirectory( tempDir ); - facade.configureInvocation( request, 0 ); - assertEquals( tempDir, request.getBaseDirectory() ); - assertEquals( null, request.getPomFile() ); - - props.setProperty( "invoker.project", tempPom.getName() ); - request.setBaseDirectory( tempDir ); - facade.configureInvocation( request, 0 ); - assertEquals( tempDir, request.getBaseDirectory() ); - assertEquals( tempPom, request.getPomFile() ); - - props.setProperty( "invoker.project", "" ); - request.setBaseDirectory( tempDir ); - facade.configureInvocation( request, 0 ); - assertEquals( tempDir, request.getBaseDirectory() ); - assertEquals( null, request.getPomFile() ); - - tempPom.delete(); - } - - public void testConfigureRequestMavenOpts() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setMavenOpts( "default" ); - facade.configureInvocation( request, 0 ); - assertEquals( "default", request.getMavenOpts() ); - - props.setProperty( "invoker.mavenOpts", "-Xmx512m" ); - facade.configureInvocation( request, 0 ); - assertEquals( "-Xmx512m", request.getMavenOpts() ); - } - - public void testConfigureRequestFailureBehavior() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setReactorFailureBehavior( InvocationRequest.ReactorFailureBehavior.FailAtEnd ); - facade.configureInvocation( request, 0 ); - assertEquals( InvocationRequest.ReactorFailureBehavior.FailAtEnd, request.getReactorFailureBehavior() ); - - props.setProperty( "invoker.failureBehavior", InvocationRequest.ReactorFailureBehavior.FailNever.getLongOption() ); - facade.configureInvocation( request, 0 ); - assertEquals( "fail-never", request.getReactorFailureBehavior().getLongOption() ); - } - - public void testConfigureRequestRecursion() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setRecursive( true ); - facade.configureInvocation( request, 0 ); - assertTrue( request.isRecursive() ); - request.setRecursive( false ); - facade.configureInvocation( request, 0 ); - assertFalse( request.isRecursive() ); - - props.setProperty( "invoker.nonRecursive", "true" ); - facade.configureInvocation( request, 0 ); - assertFalse( request.isRecursive() ); - - props.setProperty( "invoker.nonRecursive", "false" ); - facade.configureInvocation( request, 0 ); - assertTrue( request.isRecursive() ); - } - - public void testConfigureRequestOffline() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - InvocationRequest request = new DefaultInvocationRequest(); - - request.setOffline( true ); - facade.configureInvocation( request, 0 ); - assertTrue( request.isOffline() ); - request.setOffline( false ); - facade.configureInvocation( request, 0 ); - assertFalse( request.isOffline() ); - - props.setProperty( "invoker.offline", "true" ); - facade.configureInvocation( request, 0 ); - assertTrue( request.isOffline() ); - - props.setProperty( "invoker.offline", "false" ); - facade.configureInvocation( request, 0 ); - assertFalse( request.isOffline() ); - } - - public void testIsInvocationDefined() - throws Exception - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - assertFalse( facade.isInvocationDefined( 1 ) ); - - props.setProperty( "invoker.goals", "install" ); - assertFalse( facade.isInvocationDefined( 1 ) ); - - props.setProperty( "invoker.goals.2", "install" ); - assertFalse( facade.isInvocationDefined( 1 ) ); - assertTrue( facade.isInvocationDefined( 2 ) ); - assertFalse( facade.isInvocationDefined( 3 ) ); - } - - public void testIsSelectedDefined() - { - Properties props = new Properties(); - InvokerProperties facade = new InvokerProperties( props ); - - assertFalse( facade.isSelectorDefined( 1 ) ); - - props.setProperty( "invoker.java.version", "1.6+" ); - props.setProperty( "invoker.maven.version", "3.0+" ); - props.setProperty( "invoker.os.family", "windows" ); - assertFalse( facade.isSelectorDefined( 1 ) ); - - props.setProperty( "selector.2.java.version", "1.6+" ); - props.setProperty( "selector.3.maven.version", "3.0+" ); - props.setProperty( "selector.4.os.family", "windows" ); - assertFalse( facade.isSelectorDefined( 1 ) ); - assertTrue( facade.isSelectorDefined( 2 ) ); - assertTrue( facade.isSelectorDefined( 3 ) ); - assertTrue( facade.isSelectorDefined( 4 ) ); - assertFalse( facade.isSelectorDefined( 5 ) ); - } -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.Properties; + +import org.apache.maven.plugins.invoker.InvokerProperties; +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.InvocationRequest; + +import junit.framework.TestCase; + +/** + * Tests the invoker properties facade. + * + * @author Benjamin Bentmann + * @version $Id: InvokerPropertiesTest.java 1779250 2017-01-17 20:20:02Z rfscholte $ + */ +public class InvokerPropertiesTest + extends TestCase +{ + + public void testConstructorNullSafe() + throws Exception + { + InvokerProperties facade = new InvokerProperties( null ); + assertNotNull( facade.getProperties() ); + } + + public void testGetInvokerProperty() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + assertNull( facade.get( "undefined-key", 0 ) ); + + props.setProperty( "key", "value" ); + assertEquals( "value", facade.get( "key", 1 ) ); + + props.setProperty( "key.1", "another-value" ); + assertEquals( "another-value", facade.get( "key", 1 ) ); + assertEquals( "value", facade.get( "key", 2 ) ); + } + + public void testGetJobName() + throws Exception + { + Properties props = new Properties(); + final String jobName = "Build Job name"; + props.put( "invoker.name", jobName ); + InvokerProperties facade = new InvokerProperties( props ); + + assertEquals( jobName, facade.getJobName() ); + } + + public void testIsExpectedResult() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + assertTrue( facade.isExpectedResult( 0, 0 ) ); + assertFalse( facade.isExpectedResult( 1, 0 ) ); + + props.setProperty( "invoker.buildResult", "success" ); + assertTrue( facade.isExpectedResult( 0, 0 ) ); + assertFalse( facade.isExpectedResult( 1, 0 ) ); + + props.setProperty( "invoker.buildResult", "failure" ); + assertFalse( facade.isExpectedResult( 0, 0 ) ); + assertTrue( facade.isExpectedResult( 1, 0 ) ); + } + + public void testConfigureRequestGoals() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setGoals( Collections.singletonList( "test" ) ); + facade.configureInvocation( request, 0 ); + assertEquals( Collections.singletonList( "test" ), request.getGoals() ); + + props.setProperty( "invoker.goals", "verify" ); + facade.configureInvocation( request, 0 ); + assertEquals( Collections.singletonList( "verify" ), request.getGoals() ); + + props.setProperty( "invoker.goals", " " ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[0] ), request.getGoals() ); + + props.setProperty( "invoker.goals", " clean , test verify " ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[] { "clean", "test", "verify" } ), request.getGoals() ); + + props.setProperty( "invoker.goals", "" ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[0] ), request.getGoals() ); + } + + public void testConfigureRequestProfiles() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setProfiles( Collections.singletonList( "test" ) ); + facade.configureInvocation( request, 0 ); + assertEquals( Collections.singletonList( "test" ), request.getProfiles() ); + + props.setProperty( "invoker.profiles", "verify" ); + facade.configureInvocation( request, 0 ); + assertEquals( Collections.singletonList( "verify" ), request.getProfiles() ); + + props.setProperty( "invoker.profiles", " " ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[0] ), request.getProfiles() ); + + props.setProperty( "invoker.profiles", " clean , test verify ," ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[] { "clean", "test", "verify" } ), request.getProfiles() ); + + props.setProperty( "invoker.profiles", "" ); + facade.configureInvocation( request, 0 ); + assertEquals( Arrays.asList( new String[0] ), request.getProfiles() ); + } + + public void testConfigureRequestProject() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + File tempPom = File.createTempFile( "maven-invoker-plugin-test", ".pom" ); + File tempDir = tempPom.getParentFile(); + + request.setBaseDirectory( tempDir ); + facade.configureInvocation( request, 0 ); + assertEquals( tempDir, request.getBaseDirectory() ); + assertEquals( null, request.getPomFile() ); + + props.setProperty( "invoker.project", tempPom.getName() ); + request.setBaseDirectory( tempDir ); + facade.configureInvocation( request, 0 ); + assertEquals( tempDir, request.getBaseDirectory() ); + assertEquals( tempPom, request.getPomFile() ); + + props.setProperty( "invoker.project", "" ); + request.setBaseDirectory( tempDir ); + facade.configureInvocation( request, 0 ); + assertEquals( tempDir, request.getBaseDirectory() ); + assertEquals( null, request.getPomFile() ); + + tempPom.delete(); + } + + public void testConfigureRequestMavenOpts() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setMavenOpts( "default" ); + facade.configureInvocation( request, 0 ); + assertEquals( "default", request.getMavenOpts() ); + + props.setProperty( "invoker.mavenOpts", "-Xmx512m" ); + facade.configureInvocation( request, 0 ); + assertEquals( "-Xmx512m", request.getMavenOpts() ); + } + + public void testConfigureRequestFailureBehavior() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setReactorFailureBehavior( InvocationRequest.ReactorFailureBehavior.FailAtEnd ); + facade.configureInvocation( request, 0 ); + assertEquals( InvocationRequest.ReactorFailureBehavior.FailAtEnd, request.getReactorFailureBehavior() ); + + props.setProperty( "invoker.failureBehavior", InvocationRequest.ReactorFailureBehavior.FailNever.getLongOption() ); + facade.configureInvocation( request, 0 ); + assertEquals( "fail-never", request.getReactorFailureBehavior().getLongOption() ); + } + + public void testConfigureRequestRecursion() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setRecursive( true ); + facade.configureInvocation( request, 0 ); + assertTrue( request.isRecursive() ); + request.setRecursive( false ); + facade.configureInvocation( request, 0 ); + assertFalse( request.isRecursive() ); + + props.setProperty( "invoker.nonRecursive", "true" ); + facade.configureInvocation( request, 0 ); + assertFalse( request.isRecursive() ); + + props.setProperty( "invoker.nonRecursive", "false" ); + facade.configureInvocation( request, 0 ); + assertTrue( request.isRecursive() ); + } + + public void testConfigureRequestOffline() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + InvocationRequest request = new DefaultInvocationRequest(); + + request.setOffline( true ); + facade.configureInvocation( request, 0 ); + assertTrue( request.isOffline() ); + request.setOffline( false ); + facade.configureInvocation( request, 0 ); + assertFalse( request.isOffline() ); + + props.setProperty( "invoker.offline", "true" ); + facade.configureInvocation( request, 0 ); + assertTrue( request.isOffline() ); + + props.setProperty( "invoker.offline", "false" ); + facade.configureInvocation( request, 0 ); + assertFalse( request.isOffline() ); + } + + public void testIsInvocationDefined() + throws Exception + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + assertFalse( facade.isInvocationDefined( 1 ) ); + + props.setProperty( "invoker.goals", "install" ); + assertFalse( facade.isInvocationDefined( 1 ) ); + + props.setProperty( "invoker.goals.2", "install" ); + assertFalse( facade.isInvocationDefined( 1 ) ); + assertTrue( facade.isInvocationDefined( 2 ) ); + assertFalse( facade.isInvocationDefined( 3 ) ); + } + + public void testIsSelectedDefined() + { + Properties props = new Properties(); + InvokerProperties facade = new InvokerProperties( props ); + + assertFalse( facade.isSelectorDefined( 1 ) ); + + props.setProperty( "invoker.java.version", "1.6+" ); + props.setProperty( "invoker.maven.version", "3.0+" ); + props.setProperty( "invoker.os.family", "windows" ); + assertFalse( facade.isSelectorDefined( 1 ) ); + + props.setProperty( "selector.2.java.version", "1.6+" ); + props.setProperty( "selector.3.maven.version", "3.0+" ); + props.setProperty( "selector.4.os.family", "windows" ); + assertFalse( facade.isSelectorDefined( 1 ) ); + assertTrue( facade.isSelectorDefined( 2 ) ); + assertTrue( facade.isSelectorDefined( 3 ) ); + assertTrue( facade.isSelectorDefined( 4 ) ); + assertFalse( facade.isSelectorDefined( 5 ) ); + } +}
diff --git a/src/test/java/org/apache/maven/plugin/invoker/SelectorTest.java b/src/test/java/org/apache/maven/plugins/invoker/SelectorTest.java similarity index 94% rename from src/test/java/org/apache/maven/plugin/invoker/SelectorTest.java rename to src/test/java/org/apache/maven/plugins/invoker/SelectorTest.java index 2143702..70886fc 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/SelectorTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/SelectorTest.java
@@ -1,4 +1,4 @@ -package org.apache.maven.plugin.invoker; +package org.apache.maven.plugins.invoker; /* * Licensed to the Apache Software Foundation (ASF) under one @@ -22,6 +22,8 @@ import static org.junit.Assert.assertEquals; import java.util.Properties; +import org.apache.maven.plugins.invoker.InvokerProperties; +import org.apache.maven.plugins.invoker.Selector; import org.junit.Test; public class SelectorTest
diff --git a/src/test/java/org/apache/maven/plugin/invoker/SelectorUtilsTest.java b/src/test/java/org/apache/maven/plugins/invoker/SelectorUtilsTest.java similarity index 97% rename from src/test/java/org/apache/maven/plugin/invoker/SelectorUtilsTest.java rename to src/test/java/org/apache/maven/plugins/invoker/SelectorUtilsTest.java index 71d91f8..02760da 100644 --- a/src/test/java/org/apache/maven/plugin/invoker/SelectorUtilsTest.java +++ b/src/test/java/org/apache/maven/plugins/invoker/SelectorUtilsTest.java
@@ -1,90 +1,92 @@ -package org.apache.maven.plugin.invoker; - -/* - * 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. - */ - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import junit.framework.TestCase; - -/** - * Tests {@link SelectorUtils}. - * - * @author Benjamin Bentmann - */ -public class SelectorUtilsTest - extends TestCase -{ - - public void testParseList() - { - List<String> includes = new ArrayList<String>(); - List<String> excludes = new ArrayList<String>(); - - SelectorUtils.parseList( null, includes, excludes ); - - SelectorUtils.parseList( " 1.5, !1.4, 1.6+ ", includes, excludes ); - assertEquals( Arrays.asList( "1.5", "1.6+" ), includes ); - assertEquals( Arrays.asList( "1.4" ), excludes ); - } - - public void testParseVersion() - { - assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12" ) ); - - assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12+" ) ); - assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12-" ) ); - } - - public void testCompareVersions() - { - assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1, 6 ) ) == 0 ); - - assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 5 ), Arrays.asList( 1, 6 ) ) < 0 ); - assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1, 5 ) ) > 0 ); - - assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1 ), Arrays.asList( 1, 6 ) ) < 0 ); - assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1 ) ) > 0 ); - } - - public void testIsMatchingJre() - { - - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5" ) ); - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5" ) ); - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5" ) ); - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5" ) ); - - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5+" ) ); - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5+" ) ); - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5+" ) ); - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5+" ) ); - - assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5-" ) ); - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5-" ) ); - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5-" ) ); - assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5-" ) ); - - assertTrue( SelectorUtils.isJreVersion( (String) null, "1.5" ) ); - assertTrue( SelectorUtils.isJreVersion( "", "1.5" ) ); - } - -} +package org.apache.maven.plugins.invoker; + +/* + * 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. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.plugins.invoker.SelectorUtils; + +import junit.framework.TestCase; + +/** + * Tests {@link SelectorUtils}. + * + * @author Benjamin Bentmann + */ +public class SelectorUtilsTest + extends TestCase +{ + + public void testParseList() + { + List<String> includes = new ArrayList<String>(); + List<String> excludes = new ArrayList<String>(); + + SelectorUtils.parseList( null, includes, excludes ); + + SelectorUtils.parseList( " 1.5, !1.4, 1.6+ ", includes, excludes ); + assertEquals( Arrays.asList( "1.5", "1.6+" ), includes ); + assertEquals( Arrays.asList( "1.4" ), excludes ); + } + + public void testParseVersion() + { + assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12" ) ); + + assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12+" ) ); + assertEquals( Arrays.asList( 1, 6, 0, 12 ), SelectorUtils.parseVersion( "1.6.0_12-" ) ); + } + + public void testCompareVersions() + { + assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1, 6 ) ) == 0 ); + + assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 5 ), Arrays.asList( 1, 6 ) ) < 0 ); + assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1, 5 ) ) > 0 ); + + assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1 ), Arrays.asList( 1, 6 ) ) < 0 ); + assertTrue( SelectorUtils.compareVersions( Arrays.asList( 1, 6 ), Arrays.asList( 1 ) ) > 0 ); + } + + public void testIsMatchingJre() + { + + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5" ) ); + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5" ) ); + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5" ) ); + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5" ) ); + + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5+" ) ); + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5+" ) ); + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5+" ) ); + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5+" ) ); + + assertTrue( SelectorUtils.isJreVersion( Arrays.asList( 1, 4, 2, 8 ), "1.5-" ) ); + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 5 ), "1.5-" ) ); + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 5, 9 ), "1.5-" ) ); + assertFalse( SelectorUtils.isJreVersion( Arrays.asList( 1, 6 ), "1.5-" ) ); + + assertTrue( SelectorUtils.isJreVersion( (String) null, "1.5" ) ); + assertTrue( SelectorUtils.isJreVersion( "", "1.5" ) ); + } + +}