| /* |
| * 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.shared.verifier; |
| |
| /* |
| * 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 javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.parsers.SAXParserFactory; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.FilenameFilter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.nio.charset.Charset; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| import java.util.regex.Pattern; |
| |
| import org.apache.maven.settings.building.DefaultSettingsBuilder; |
| import org.apache.maven.settings.building.DefaultSettingsBuilderFactory; |
| import org.apache.maven.settings.building.DefaultSettingsBuildingRequest; |
| import org.apache.maven.settings.building.SettingsBuildingException; |
| import org.apache.maven.settings.building.SettingsBuildingRequest; |
| import org.apache.maven.settings.building.SettingsBuildingResult; |
| import org.apache.maven.shared.utils.StringUtils; |
| import org.apache.maven.shared.utils.io.FileUtils; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * Maven test harness for integration tests. |
| * |
| * <p><strong>DEPRECATION NOTICE:</strong> This class is deprecated and will be removed in a future version. |
| * Please migrate to |
| * <a href="https://github.com/apache/maven/tree/master/impl/maven-executor">maven-executor</a> |
| * which provides a unified, modern API for programmatic Maven execution with support for both |
| * Maven 3.9+ and Maven 4+.</p> |
| * |
| * <p>Benefits of maven-executor:</p> |
| * <ul> |
| * <li>Unified API that doesn't require updates when Maven CLI changes</li> |
| * <li>Support for both forked and embedded execution modes</li> |
| * <li>Dependency-less design</li> |
| * <li>Better environment isolation</li> |
| * <li>Transparent support for Maven 3.9+ and Maven 4+</li> |
| * </ul> |
| * |
| * <p>Migration example:</p> |
| * <pre>{@code |
| * // Old (maven-verifier): |
| * Verifier verifier = new Verifier("/path/to/project"); |
| * verifier.addCliArgument("package"); |
| * verifier.execute(); |
| * verifier.verifyErrorFreeLog(); |
| * |
| * // New (maven-executor): |
| * Executor executor = new ForkedExecutor(); |
| * ExecutorRequest request = ExecutorRequest.builder() |
| * .cwd(Paths.get("/path/to/project")) |
| * .arguments(List.of("package")) |
| * .build(); |
| * int exitCode = executor.execute(request); |
| * assertEquals(0, exitCode); |
| * }</pre> |
| * |
| * @see <a href="https://github.com/apache/maven-verifier/blob/master/MIGRATION.md">Migration Guide</a> |
| * @see <a href="https://github.com/apache/maven/tree/master/impl/maven-executor">maven-executor</a> |
| * @author Jason van Zyl |
| * @author <a href="mailto:brett@apache.org">Brett Porter</a> |
| * @deprecated Use <a href="https://github.com/apache/maven/tree/master/impl/maven-executor">maven-executor</a> instead. |
| * See <a href="https://github.com/apache/maven-verifier/blob/master/MIGRATION.md">Migration Guide</a>. |
| */ |
| @Deprecated |
| public class Verifier { |
| private static final String LOG_FILENAME = "log.txt"; |
| |
| private static final String[] DEFAULT_CLI_ARGUMENTS = {"-e", "--batch-mode"}; |
| |
| /** |
| * Command used to clean project before execution. |
| * <p |
| * NOTE: Neither test lifecycle binding nor prefix resolution here but call the goal directly. |
| */ |
| private static final String CLEAN_CLI_ARGUMENT = "org.apache.maven.plugins:maven-clean-plugin:clean"; |
| |
| public static final String USER_HOME = System.getProperty("user.home"); |
| |
| public static final File USER_MAVEN_CONFIGURATION_HOME = new File(USER_HOME, ".m2"); |
| |
| public static final File DEFAULT_USER_SETTINGS_FILE = new File(USER_MAVEN_CONFIGURATION_HOME, "settings.xml"); |
| |
| public static final File DEFAULT_GLOBAL_SETTINGS_FILE = new File(System.getProperty("maven.conf"), "settings.xml"); |
| |
| private String localRepo; |
| |
| private final String basedir; |
| |
| private String[] defaultCliArguments; |
| |
| private List<String> cliArguments = new ArrayList<>(); |
| |
| private Properties systemProperties = new Properties(); |
| |
| private Map<String, String> environmentVariables = new HashMap<>(); |
| |
| private Properties verifierProperties = new Properties(); |
| |
| private boolean autoclean = true; |
| |
| private String localRepoLayout = "default"; |
| |
| /** |
| * If {@code true} uses {@link ForkedLauncher}, if {@code false} uses {@link Embedded3xLauncher}, |
| * otherwise considers the value {@link #forkMode}. |
| */ |
| private Boolean forkJvm; |
| |
| private String logFileName = LOG_FILENAME; |
| |
| private File logFile; |
| |
| private String mavenHome; |
| |
| private String mvnExecutable; |
| |
| // will launch mvn with -X |
| private boolean mavenDebug = false; |
| |
| /** |
| * Either "auto" (use {@link ForkedLauncher} when {@link #environmentVariables} is not empty, |
| * otherwise use {@link Embedded3xLauncher}) , "embedder" (always use {@link Embedded3xLauncher}) |
| * or something else (always use {@link ForkedLauncher}). |
| * Set through system property {@code verifier.forkMode}. |
| * Only relevant if {@link #forkJvm} is {@code null}. |
| */ |
| private String forkMode; |
| |
| private boolean debugJvm = false; |
| |
| private boolean useWrapper; |
| |
| private static MavenLauncher embeddedLauncher; |
| |
| private String settingsFile; |
| |
| public Verifier(String basedir) throws VerificationException { |
| this.basedir = basedir; |
| |
| this.forkMode = System.getProperty("verifier.forkMode"); |
| |
| findLocalRepo(settingsFile); |
| |
| this.mavenHome = System.getProperty("maven.home"); |
| |
| setForkMode(); |
| |
| useWrapper = Files.exists(Paths.get(getBasedir(), "mvnw")); |
| |
| this.defaultCliArguments = DEFAULT_CLI_ARGUMENTS.clone(); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, boolean debug) throws VerificationException { |
| this(basedir); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile) throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug) throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <uL> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * <li>{@link #setDefaultCliArguments(String[] defaultCliArguments)} to set default cliArguments</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug, String[] defaultCliArguments) |
| throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| setDefaultCliArguments(defaultCliArguments); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String)}</li> |
| * <li>{@link #setSettingsFile(String)} to set settings file</li> |
| * <li>{@link #setForkJvm(boolean)} to set forkJvm status</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug, boolean forkJvm) throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| setForkJvm(forkJvm); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * <li>{@link #setForkJvm(boolean)} to set forkJvm status and</li> |
| * <li>{@link #setDefaultCliArguments(String[] defaultCliArguments)} to set settings file</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug, boolean forkJvm, String[] defaultCliArguments) |
| throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| setForkJvm(forkJvm); |
| setDefaultCliArguments(defaultCliArguments); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * <li>{@link #setMavenHome(String mavenHome)} to set maven home</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug, String mavenHome) throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| setMavenHome(mavenHome); |
| } |
| |
| /** |
| * @deprecated to be removed |
| * use <ul> |
| * <li>{@link #Verifier(String basedir)}</li> |
| * <li>{@link #setSettingsFile(String settingsFile)} to set settings file</li> |
| * <li>{@link #setMavenHome(String mavenHome)} to set maven home</li> |
| * <li>{@link #setDefaultCliArguments(String[] defaultCliArguments)} to set settings file</li> |
| * </ul> |
| */ |
| @Deprecated |
| public Verifier(String basedir, String settingsFile, boolean debug, String mavenHome, String[] defaultCliArguments) |
| throws VerificationException { |
| this(basedir); |
| setSettingsFile(settingsFile); |
| setDefaultCliArguments(defaultCliArguments); |
| setMavenHome(mavenHome); |
| } |
| |
| public void setLocalRepo(String localRepo) { |
| this.localRepo = localRepo; |
| } |
| |
| /** |
| * @deprecated will be removed without replacement |
| */ |
| @Deprecated |
| public void resetStreams() {} |
| |
| /** |
| * @deprecated will be removed without replacement |
| */ |
| @Deprecated |
| public void displayStreamBuffers() {} |
| |
| // ---------------------------------------------------------------------- |
| // |
| // ---------------------------------------------------------------------- |
| |
| public void verify(boolean chokeOnErrorOutput) throws VerificationException { |
| List<String> lines = loadFile(getBasedir(), "expected-results.txt", false); |
| |
| for (String line : lines) { |
| verifyExpectedResult(line); |
| } |
| |
| if (chokeOnErrorOutput) { |
| verifyErrorFreeLog(); |
| } |
| } |
| |
| public void verifyErrorFreeLog() throws VerificationException { |
| List<String> lines = loadFile(getLogFile(), false); |
| |
| for (String line : lines) { |
| // A hack to keep stupid velocity resource loader errors from triggering failure |
| if (stripAnsi(line).contains("[ERROR]") && !isVelocityError(line)) { |
| throw new VerificationException("Error in execution: " + line); |
| } |
| } |
| } |
| |
| /** |
| * Checks whether the specified line is just an error message from Velocity. Especially old versions of Doxia employ |
| * a very noisy Velocity instance. |
| * |
| * @param line The log line to check, must not be <code>null</code>. |
| * @return <code>true</code> if the line appears to be a Velocity error, <code>false</code> otherwise. |
| */ |
| private static boolean isVelocityError(String line) { |
| return line.contains("VM_global_library.vm") || line.contains("VM #") && line.contains("macro"); |
| } |
| |
| /** |
| * Throws an exception if the text is not present in the log. |
| * |
| * @param text the text to assert present |
| * @throws VerificationException if text is not found in log |
| */ |
| public void verifyTextInLog(String text) throws VerificationException { |
| List<String> lines = loadFile(getLogFile(), false); |
| |
| boolean result = false; |
| for (String line : lines) { |
| if (stripAnsi(line).contains(text)) { |
| result = true; |
| break; |
| } |
| } |
| if (!result) { |
| throw new VerificationException("Text not found in log: " + text); |
| } |
| } |
| |
| public static String stripAnsi(String msg) { |
| return msg.replaceAll("\u001B\\[[;\\d]*[ -/]*[@-~]", ""); |
| } |
| |
| public Properties loadProperties(String filename) throws VerificationException { |
| Properties properties = new Properties(); |
| |
| File propertiesFile = new File(getBasedir(), filename); |
| try (FileInputStream fis = new FileInputStream(propertiesFile)) { |
| properties.load(fis); |
| } catch (IOException e) { |
| throw new VerificationException("Error reading properties file", e); |
| } |
| |
| return properties; |
| } |
| |
| /** |
| * Loads the (non-empty) lines of the specified text file. |
| * |
| * @param filename The path to the text file to load, relative to the base directory, must not be <code>null</code>. |
| * @param encoding The character encoding of the file, may be <code>null</code> or empty to use the platform default |
| * encoding. |
| * @return The list of (non-empty) lines from the text file, can be empty but never <code>null</code>. |
| * @throws IOException If the file could not be loaded. |
| * @since 1.2 |
| */ |
| public List<String> loadLines(String filename, String encoding) throws IOException { |
| List<String> lines = new ArrayList<>(); |
| |
| try (BufferedReader reader = getReader(filename, encoding)) { |
| String line; |
| while ((line = reader.readLine()) != null) { |
| if (line.length() > 0) { |
| lines.add(line); |
| } |
| } |
| } |
| |
| return lines; |
| } |
| |
| private BufferedReader getReader(String filename, String encoding) throws IOException { |
| File file = new File(getBasedir(), filename); |
| |
| if (encoding != null && !encoding.isEmpty()) { |
| return Files.newBufferedReader(file.toPath(), Charset.forName(encoding)); |
| } else { |
| return Files.newBufferedReader(file.toPath()); |
| } |
| } |
| |
| public List<String> loadFile(String basedir, String filename, boolean hasCommand) throws VerificationException { |
| return loadFile(new File(basedir, filename), hasCommand); |
| } |
| |
| public List<String> loadFile(File file, boolean hasCommand) throws VerificationException { |
| List<String> lines = new ArrayList<>(); |
| |
| if (file.exists()) { |
| try (BufferedReader reader = new BufferedReader(new FileReader(file))) { |
| String line = reader.readLine(); |
| |
| while (line != null) { |
| line = line.trim(); |
| |
| if (!line.startsWith("#") && line.length() != 0) { |
| lines.addAll(replaceArtifacts(line, hasCommand)); |
| } |
| line = reader.readLine(); |
| } |
| } catch (IOException e) { |
| throw new VerificationException(e); |
| } |
| } |
| |
| return lines; |
| } |
| |
| private static final String MARKER = "${artifact:"; |
| |
| private List<String> replaceArtifacts(String line, boolean hasCommand) { |
| int index = line.indexOf(MARKER); |
| if (index >= 0) { |
| String newLine = line.substring(0, index); |
| index = line.indexOf("}", index); |
| if (index < 0) { |
| throw new IllegalArgumentException("line does not contain ending artifact marker: '" + line + "'"); |
| } |
| String artifact = line.substring(newLine.length() + MARKER.length(), index); |
| |
| newLine += getArtifactPath(artifact); |
| newLine += line.substring(index + 1); |
| |
| List<String> l = new ArrayList<>(); |
| l.add(newLine); |
| |
| int endIndex = newLine.lastIndexOf('/'); |
| |
| String command = null; |
| String filespec; |
| if (hasCommand) { |
| int startIndex = newLine.indexOf(' '); |
| |
| command = newLine.substring(0, startIndex); |
| |
| filespec = newLine.substring(startIndex + 1, endIndex); |
| } else { |
| filespec = newLine; |
| } |
| |
| File dir = new File(filespec); |
| addMetadataToList(dir, hasCommand, l, command); |
| addMetadataToList(dir.getParentFile(), hasCommand, l, command); |
| |
| return l; |
| } else { |
| return Collections.singletonList(line); |
| } |
| } |
| |
| private static void addMetadataToList(File dir, boolean hasCommand, List<String> l, String command) { |
| if (dir.exists() && dir.isDirectory()) { |
| String[] files = dir.list(new FilenameFilter() { |
| public boolean accept(File dir, String name) { |
| return name.startsWith("maven-metadata") && name.endsWith(".xml"); |
| } |
| }); |
| |
| for (String file : files) { |
| if (hasCommand) { |
| l.add(command + " " + new File(dir, file).getPath()); |
| } else { |
| l.add(new File(dir, file).getPath()); |
| } |
| } |
| } |
| } |
| |
| private String getArtifactPath(String artifact) { |
| StringTokenizer tok = new StringTokenizer(artifact, ":"); |
| if (tok.countTokens() != 4) { |
| throw new IllegalArgumentException("Artifact must have 4 tokens: '" + artifact + "'"); |
| } |
| |
| String[] a = new String[4]; |
| for (int i = 0; i < 4; i++) { |
| a[i] = tok.nextToken(); |
| } |
| |
| String groupId = a[0]; |
| String artifactId = a[1]; |
| String version = a[2]; |
| String ext = a[3]; |
| return getArtifactPath(groupId, artifactId, version, ext); |
| } |
| |
| public String getArtifactPath(String groupId, String artifactId, String version, String ext) { |
| return getArtifactPath(groupId, artifactId, version, ext, null); |
| } |
| |
| /** |
| * Returns the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier. |
| * |
| * @param gid The groupId, must not be null. |
| * @param aid The artifactId, must not be null. |
| * @param version The version, must not be null. |
| * @param ext The extension, must not be null. |
| * @param classifier The classifier, may be null to be omitted. |
| * @return the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier, |
| * never null. |
| */ |
| public String getArtifactPath(String gid, String aid, String version, String ext, String classifier) { |
| if (classifier != null && classifier.length() == 0) { |
| classifier = null; |
| } |
| if ("maven-plugin".equals(ext)) { |
| ext = "jar"; |
| } |
| if ("coreit-artifact".equals(ext)) { |
| ext = "jar"; |
| classifier = "it"; |
| } |
| if ("test-jar".equals(ext)) { |
| ext = "jar"; |
| classifier = "tests"; |
| } |
| |
| String repositoryPath; |
| if ("legacy".equals(localRepoLayout)) { |
| repositoryPath = gid + "/" + ext + "s/" + aid + "-" + version + "." + ext; |
| } else if ("default".equals(localRepoLayout)) { |
| repositoryPath = gid.replace('.', '/'); |
| repositoryPath = repositoryPath + "/" + aid + "/" + version; |
| repositoryPath = repositoryPath + "/" + aid + "-" + version; |
| if (classifier != null) { |
| repositoryPath = repositoryPath + "-" + classifier; |
| } |
| repositoryPath = repositoryPath + "." + ext; |
| } else { |
| throw new IllegalStateException("Unknown layout: " + localRepoLayout); |
| } |
| |
| return localRepo + "/" + repositoryPath; |
| } |
| |
| public List<String> getArtifactFileNameList(String org, String name, String version, String ext) { |
| List<String> files = new ArrayList<>(); |
| String artifactPath = getArtifactPath(org, name, version, ext); |
| File dir = new File(artifactPath); |
| files.add(artifactPath); |
| addMetadataToList(dir, false, files, null); |
| addMetadataToList(dir.getParentFile(), false, files, null); |
| return files; |
| } |
| |
| /** |
| * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path |
| * actually points to existing metadata. |
| * |
| * @param gid The group id, must not be <code>null</code>. |
| * @param aid The artifact id, must not be <code>null</code>. |
| * @param version The artifact version, may be <code>null</code>. |
| * @return The (absolute) path to the local artifact metadata, never <code>null</code>. |
| */ |
| public String getArtifactMetadataPath(String gid, String aid, String version) { |
| return getArtifactMetadataPath(gid, aid, version, "maven-metadata-local.xml"); |
| } |
| |
| /** |
| * Gets the path to a file in the local artifact directory. Note that the method does not check whether the returned |
| * path actually points to an existing file. |
| * |
| * @param gid The group id, must not be <code>null</code>. |
| * @param aid The artifact id, may be <code>null</code>. |
| * @param version The artifact version, may be <code>null</code>. |
| * @param filename The filename to use, must not be <code>null</code>. |
| * @return The (absolute) path to the local artifact metadata, never <code>null</code>. |
| */ |
| public String getArtifactMetadataPath(String gid, String aid, String version, String filename) { |
| StringBuilder buffer = new StringBuilder(256); |
| |
| buffer.append(localRepo); |
| buffer.append('/'); |
| |
| if ("default".equals(localRepoLayout)) { |
| buffer.append(gid.replace('.', '/')); |
| buffer.append('/'); |
| |
| if (aid != null) { |
| buffer.append(aid); |
| buffer.append('/'); |
| |
| if (version != null) { |
| buffer.append(version); |
| buffer.append('/'); |
| } |
| } |
| |
| buffer.append(filename); |
| } else { |
| throw new IllegalStateException("Unsupported repository layout: " + localRepoLayout); |
| } |
| |
| return buffer.toString(); |
| } |
| |
| /** |
| * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path |
| * actually points to existing metadata. |
| * |
| * @param gid The group id, must not be <code>null</code>. |
| * @param aid The artifact id, must not be <code>null</code>. |
| * @return The (absolute) path to the local artifact metadata, never <code>null</code>. |
| */ |
| public String getArtifactMetadataPath(String gid, String aid) { |
| return getArtifactMetadataPath(gid, aid, null); |
| } |
| |
| static String retrieveLocalRepo(String settingsXmlPath) throws SettingsBuildingException { |
| DefaultSettingsBuilderFactory settingsBuilderFactory = new DefaultSettingsBuilderFactory(); |
| DefaultSettingsBuilder settingsBuilder = settingsBuilderFactory.newInstance(); |
| |
| File userSettingsFile; |
| if (settingsXmlPath != null) { |
| userSettingsFile = new File(settingsXmlPath); |
| } else { |
| userSettingsFile = DEFAULT_USER_SETTINGS_FILE; |
| } |
| |
| SettingsBuildingRequest settingsBuildingRequest = new DefaultSettingsBuildingRequest(); |
| settingsBuildingRequest.setGlobalSettingsFile(DEFAULT_GLOBAL_SETTINGS_FILE); |
| settingsBuildingRequest.setUserSettingsFile(userSettingsFile); |
| settingsBuildingRequest.setSystemProperties(System.getProperties()); |
| |
| // takes care of interpolation and merging |
| SettingsBuildingResult result = settingsBuilder.build(settingsBuildingRequest); |
| return result.getEffectiveSettings().getLocalRepository(); |
| } |
| |
| public void deleteArtifact(String org, String name, String version, String ext) throws IOException { |
| List<String> files = getArtifactFileNameList(org, name, version, ext); |
| for (String fileName : files) { |
| FileUtils.forceDelete(new File(fileName)); |
| } |
| } |
| |
| /** |
| * Deletes all artifacts in the specified group id from the local repository. |
| * |
| * @param gid The group id whose artifacts should be deleted, must not be <code>null</code>. |
| * @throws IOException If the artifacts could not be deleted. |
| * @since 1.2 |
| */ |
| public void deleteArtifacts(String gid) throws IOException { |
| String path; |
| if ("default".equals(localRepoLayout)) { |
| path = gid.replace('.', '/'); |
| } else if ("legacy".equals(localRepoLayout)) { |
| path = gid; |
| } else { |
| throw new IllegalStateException("Unsupported repository layout: " + localRepoLayout); |
| } |
| |
| FileUtils.deleteDirectory(new File(localRepo, path)); |
| } |
| |
| /** |
| * Deletes all artifacts in the specified g:a:v from the local repository. |
| * |
| * @param gid The group id whose artifacts should be deleted, must not be <code>null</code>. |
| * @param aid The artifact id whose artifacts should be deleted, must not be <code>null</code>. |
| * @param version The (base) version whose artifacts should be deleted, must not be <code>null</code>. |
| * @throws IOException If the artifacts could not be deleted. |
| * @since 1.3 |
| */ |
| public void deleteArtifacts(String gid, String aid, String version) throws IOException { |
| String path; |
| if ("default".equals(localRepoLayout)) { |
| path = gid.replace('.', '/') + '/' + aid + '/' + version; |
| } else { |
| throw new IllegalStateException("Unsupported repository layout: " + localRepoLayout); |
| } |
| |
| FileUtils.deleteDirectory(new File(localRepo, path)); |
| } |
| |
| /** |
| * Deletes the specified directory. |
| * |
| * @param path The path to the directory to delete, relative to the base directory, must not be <code>null</code>. |
| * @throws IOException If the directory could not be deleted. |
| * @since 1.2 |
| */ |
| public void deleteDirectory(String path) throws IOException { |
| FileUtils.deleteDirectory(new File(getBasedir(), path)); |
| } |
| |
| /** |
| * Writes a text file with the specified contents. The contents will be encoded using UTF-8. |
| * |
| * @param path The path to the file, relative to the base directory, must not be <code>null</code>. |
| * @param contents The contents to write, must not be <code>null</code>. |
| * @throws IOException If the file could not be written. |
| * @since 1.2 |
| */ |
| public void writeFile(String path, String contents) throws IOException { |
| FileUtils.fileWrite(new File(getBasedir(), path).getAbsolutePath(), "UTF-8", contents); |
| } |
| |
| /** |
| * Filters a text file by replacing some user-defined tokens. |
| * This method is equivalent to: |
| * |
| * <pre> |
| * filterFile( srcPath, dstPath, fileEncoding, verifier.newDefaultFilterMap() ) |
| * </pre> |
| * |
| * @param srcPath The path to the input file, relative to the base directory, must not be |
| * <code>null</code>. |
| * @param dstPath The path to the output file, relative to the base directory and possibly equal to the |
| * input file, must not be <code>null</code>. |
| * @param fileEncoding The file encoding to use, may be <code>null</code> or empty to use the platform's default |
| * encoding. |
| * @return The path to the filtered output file, never <code>null</code>. |
| * @throws IOException If the file could not be filtered. |
| * @since 2.0 |
| */ |
| public File filterFile(String srcPath, String dstPath, String fileEncoding) throws IOException { |
| return filterFile(srcPath, dstPath, fileEncoding, newDefaultFilterMap()); |
| } |
| |
| /** |
| * Filters a text file by replacing some user-defined tokens. |
| * |
| * @param srcPath The path to the input file, relative to the base directory, must not be |
| * <code>null</code>. |
| * @param dstPath The path to the output file, relative to the base directory and possibly equal to the |
| * input file, must not be <code>null</code>. |
| * @param fileEncoding The file encoding to use, may be <code>null</code> or empty to use the platform's default |
| * encoding. |
| * @param filterMap The mapping from tokens to replacement values, must not be <code>null</code>. |
| * @return The path to the filtered output file, never <code>null</code>. |
| * @throws IOException If the file could not be filtered. |
| * @since 1.2 |
| */ |
| public File filterFile(String srcPath, String dstPath, String fileEncoding, Map<String, String> filterMap) |
| throws IOException { |
| File srcFile = new File(getBasedir(), srcPath); |
| String data = FileUtils.fileRead(srcFile, fileEncoding); |
| |
| for (Map.Entry<String, String> entry : filterMap.entrySet()) { |
| data = StringUtils.replace(data, entry.getKey(), entry.getValue()); |
| } |
| |
| File dstFile = new File(getBasedir(), dstPath); |
| //noinspection ResultOfMethodCallIgnored |
| dstFile.getParentFile().mkdirs(); |
| FileUtils.fileWrite(dstFile.getPath(), fileEncoding, data); |
| |
| return dstFile; |
| } |
| |
| /** |
| * There are 226 references to this method in Maven core ITs. In most (all?) cases it is used together with |
| * {@link #newDefaultFilterProperties()}. Need to remove both methods and update all clients eventually/ |
| * |
| * @param srcPath The path to the input file, relative to the base directory, must not be |
| * <code>null</code>. |
| * @param dstPath The path to the output file, relative to the base directory and possibly equal to the |
| * input file, must not be <code>null</code>. |
| * @param fileEncoding The file encoding to use, may be <code>null</code> or empty to use the platform's default |
| * encoding. |
| * @param filterProperties The mapping from tokens to replacement values, must not be <code>null</code>. |
| * @return The path to the filtered output file, never <code>null</code>. |
| * @throws IOException If the file could not be filtered. |
| * @deprecated use {@link #filterFile(String, String, String, Map)} |
| */ |
| @Deprecated |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public File filterFile(String srcPath, String dstPath, String fileEncoding, Properties filterProperties) |
| throws IOException { |
| return filterFile(srcPath, dstPath, fileEncoding, (Map) filterProperties); |
| } |
| |
| /** |
| * Gets a new copy of the default filter properties. These default filter properties map the tokens "@basedir@" and |
| * "@baseurl@" to the test's base directory and its base <code>file:</code> URL, respectively. |
| * |
| * @return The (modifiable) map with the default filter properties, never <code>null</code>. |
| * @since 1.2 |
| * @deprecated use {@link #newDefaultFilterMap()} |
| */ |
| @Deprecated |
| public Properties newDefaultFilterProperties() { |
| Properties filterProperties = new Properties(); |
| filterProperties.putAll(newDefaultFilterMap()); |
| return filterProperties; |
| } |
| |
| /** |
| * Gets a new copy of the default filter map. These default filter map, contains the tokens "@basedir@" and |
| * "@baseurl@" to the test's base directory and its base <code>file:</code> URL, respectively. |
| * |
| * @return The (modifiable) map with the default filter map, never <code>null</code>. |
| * @since 2.0 |
| */ |
| public Map<String, String> newDefaultFilterMap() { |
| Map<String, String> filterMap = new HashMap<>(); |
| |
| Path basedir = Paths.get(getBasedir()).toAbsolutePath(); |
| filterMap.put("@basedir@", basedir.toString()); |
| filterMap.put("@baseurl@", basedir.toUri().toASCIIString()); |
| |
| return filterMap; |
| } |
| |
| /** |
| * Verifies that the given file exists. |
| * |
| * @param file the path of the file to check |
| * @throws VerificationException in case the given file does not exist |
| */ |
| public void verifyFilePresent(String file) throws VerificationException { |
| verifyFilePresence(file, true); |
| } |
| |
| /** |
| * Verifies the given file's content matches an regular expression. |
| * Note this method also checks that the file exists and is readable. |
| * |
| * @param file the path of the file to check |
| * @param regex a regular expression |
| * @throws VerificationException in case the file was not found or its content does not match the given pattern |
| * @see Pattern |
| */ |
| public void verifyFileContentMatches(String file, String regex) throws VerificationException { |
| verifyFilePresent(file); |
| try { |
| String content = FileUtils.fileRead(file); |
| if (!Pattern.matches(regex, content)) { |
| throw new VerificationException("Content of " + file + " does not match " + regex); |
| } |
| } catch (IOException e) { |
| throw new VerificationException("Could not read from " + file, e); |
| } |
| } |
| |
| /** |
| * Verifies that the given file does not exist. |
| * |
| * @param file the path of the file to check |
| * @throws VerificationException if the given file exists |
| */ |
| public void verifyFileNotPresent(String file) throws VerificationException { |
| verifyFilePresence(file, false); |
| } |
| |
| private void verifyArtifactPresence(boolean wanted, String groupId, String artifactId, String version, String ext) |
| throws VerificationException { |
| List<String> files = getArtifactFileNameList(groupId, artifactId, version, ext); |
| for (String fileName : files) { |
| verifyFilePresence(fileName, wanted); |
| } |
| } |
| |
| /** |
| * Verifies that the artifact given through its Maven coordinates exists. |
| * |
| * @param groupId the groupId of the artifact (must not be null) |
| * @param artifactId the artifactId of the artifact (must not be null) |
| * @param version the version of the artifact (must not be null) |
| * @param ext the extension of the artifact (must not be null) |
| * @throws VerificationException if the given artifact does not exist |
| */ |
| public void verifyArtifactPresent(String groupId, String artifactId, String version, String ext) |
| throws VerificationException { |
| verifyArtifactPresence(true, groupId, artifactId, version, ext); |
| } |
| |
| /** |
| * Verifies that the artifact given through its Maven coordinates does not exist. |
| * |
| * @param groupId the groupId of the artifact (must not be null) |
| * @param artifactId the artifactId of the artifact (must not be null) |
| * @param version the version of the artifact (must not be null) |
| * @param ext the extension of the artifact (must not be null) |
| * @throws VerificationException if the given artifact exists |
| */ |
| public void verifyArtifactNotPresent(String groupId, String artifactId, String version, String ext) |
| throws VerificationException { |
| verifyArtifactPresence(false, groupId, artifactId, version, ext); |
| } |
| |
| private void verifyExpectedResult(String line) throws VerificationException { |
| boolean wanted = true; |
| if (line.startsWith("!")) { |
| line = line.substring(1); |
| wanted = false; |
| } |
| |
| verifyFilePresence(line, wanted); |
| } |
| |
| private void verifyFilePresence(String filePath, boolean wanted) throws VerificationException { |
| if (filePath.contains("!/")) { |
| Path basedir = Paths.get(getBasedir()).toAbsolutePath(); |
| String urlString = "jar:" + basedir.toUri().toASCIIString() + "/" + filePath; |
| |
| InputStream is = null; |
| try { |
| URL url = new URL(urlString); |
| |
| is = url.openStream(); |
| |
| if (is == null) { |
| if (wanted) { |
| throw new VerificationException("Expected JAR resource was not found: " + filePath); |
| } |
| } else { |
| if (!wanted) { |
| throw new VerificationException("Unwanted JAR resource was found: " + filePath); |
| } |
| } |
| } catch (MalformedURLException e) { |
| throw new VerificationException("Error looking for JAR resource", e); |
| } catch (IOException e) { |
| if (wanted) { |
| throw new VerificationException("Error looking for JAR resource: " + filePath); |
| } |
| } finally { |
| if (is != null) { |
| try { |
| is.close(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| } else { |
| File expectedFile = new File(filePath); |
| |
| // NOTE: On Windows, a path with a leading (back-)slash is relative to the current drive |
| if (!expectedFile.isAbsolute() && !expectedFile.getPath().startsWith(File.separator)) { |
| expectedFile = new File(getBasedir(), filePath); |
| } |
| |
| if (filePath.indexOf('*') > -1) { |
| File parent = expectedFile.getParentFile(); |
| |
| if (!parent.exists()) { |
| if (wanted) { |
| throw new VerificationException( |
| "Expected file pattern was not found: " + expectedFile.getPath()); |
| } |
| } else { |
| String shortNamePattern = expectedFile.getName().replaceAll("\\*", ".*"); |
| |
| String[] candidates = parent.list(); |
| |
| boolean found = false; |
| |
| if (candidates != null) { |
| for (String candidate : candidates) { |
| if (candidate.matches(shortNamePattern)) { |
| found = true; |
| break; |
| } |
| } |
| } |
| |
| if (!found && wanted) { |
| throw new VerificationException( |
| "Expected file pattern was not found: " + expectedFile.getPath()); |
| } else if (found && !wanted) { |
| throw new VerificationException("Unwanted file pattern was found: " + expectedFile.getPath()); |
| } |
| } |
| } else { |
| if (!expectedFile.exists()) { |
| if (wanted) { |
| throw new VerificationException("Expected file was not found: " + expectedFile.getPath()); |
| } |
| } else { |
| if (!wanted) { |
| throw new VerificationException("Unwanted file was found: " + expectedFile.getPath()); |
| } |
| } |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| // |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Execute Maven. |
| * |
| * @deprecated will be removed. |
| * <p> |
| * For replacement please use: |
| * <pre> |
| * verifier.addCliArgument( "goal" ); |
| * verifier.execute(); |
| * </pre> |
| */ |
| @Deprecated |
| public void executeGoal(String goal) throws VerificationException { |
| executeGoal(goal, environmentVariables); |
| } |
| |
| /** |
| * Execute Maven. |
| * |
| * @deprecated will be removed. |
| * <p> |
| * For replacement please use: |
| * <pre> |
| * verifier.addCliArgument( "goal" ); |
| * verifier.setEnvironmentVariable( "key1", "value1" ); |
| * verifier.setEnvironmentVariable( "key2", "value2" ); |
| * verifier.execute(); |
| * </pre> |
| */ |
| @Deprecated |
| public void executeGoal(String goal, Map<String, String> envVars) throws VerificationException { |
| executeGoals(Collections.singletonList(goal), envVars); |
| } |
| |
| /** |
| * Execute Maven. |
| * |
| * @deprecated will be removed |
| * <p> |
| * For replacement please use: |
| * <pre> |
| * verifier.addCliArguments( "goal1", "goal2" ); |
| * verifier.execute(); |
| * </pre> |
| */ |
| @Deprecated |
| public void executeGoals(List<String> goals) throws VerificationException { |
| executeGoals(goals, environmentVariables); |
| } |
| |
| public String getExecutable() { |
| // Use a strategy for finding the maven executable, John has a simple method like this |
| // but a little strategy + chain of command would be nicer. |
| |
| if (mavenHome != null) { |
| return mavenHome + "/bin/mvn"; |
| } else { |
| return "mvn"; |
| } |
| } |
| |
| /** |
| * Execute Maven. |
| * |
| * @deprecated will be removed |
| * <p> |
| * For replacement please use: |
| * <pre> |
| * verifier.addCliArguments( "goal1", "goal2" ); |
| * verifier.setEnvironmentVariable( "key1", "value1" ); |
| * verifier.setEnvironmentVariable( "key2", "value2" ); |
| * verifier.execute(); |
| * </pre> |
| */ |
| @Deprecated |
| public void executeGoals(List<String> goals, Map<String, String> envVars) throws VerificationException { |
| cliArguments.addAll(goals); |
| environmentVariables.putAll(envVars); |
| execute(); |
| } |
| |
| /** |
| * Execute Maven. |
| */ |
| public void execute() throws VerificationException { |
| |
| List<String> args = new ArrayList<>(); |
| |
| Collections.addAll(args, defaultCliArguments); |
| |
| if (this.mavenDebug) { |
| args.add("-X"); |
| } |
| |
| /* |
| * NOTE: Unless explicitly requested by the caller, the forked builds should use the current local |
| * repository. Otherwise, the forked builds would in principle leave the sandbox environment which has been |
| * setup for the current build. In particular, using "maven.repo.local" will make sure the forked builds use |
| * the same local repo as the parent build even if a custom user settings is provided. |
| */ |
| boolean useMavenRepoLocal = Boolean.valueOf(verifierProperties.getProperty("use.mavenRepoLocal", "true")); |
| |
| if (useMavenRepoLocal) { |
| args.add("-Dmaven.repo.local=" + localRepo); |
| } |
| |
| if (autoclean) { |
| args.add(CLEAN_CLI_ARGUMENT); |
| } |
| |
| for (String cliArgument : cliArguments) { |
| args.add(cliArgument.replace("${basedir}", getBasedir())); |
| } |
| |
| int ret; |
| File logFile = getLogFile(); |
| |
| try { |
| MavenLauncher launcher = getMavenLauncher(environmentVariables); |
| |
| String[] cliArgs = args.toArray(new String[0]); |
| ret = launcher.run(cliArgs, systemProperties, getBasedir(), logFile); |
| } catch (LauncherException e) { |
| throw new VerificationException("Failed to execute Maven", e); |
| } catch (IOException e) { |
| throw new VerificationException(e); |
| } |
| |
| if (ret > 0) { |
| throw new VerificationException("Exit code was non-zero: " + ret + "; command line and log = \n" |
| + new File(mavenHome, "bin/mvn") + " " |
| + StringUtils.join(args.iterator(), " ") + "\n" + getLogContents(logFile)); |
| } |
| } |
| |
| protected MavenLauncher getMavenLauncher(Map<String, String> envVars) throws LauncherException { |
| boolean fork; |
| if (useWrapper) { |
| fork = true; |
| } else if (forkJvm != null) { |
| fork = forkJvm; |
| } else if ((envVars.isEmpty() && "auto".equalsIgnoreCase(forkMode)) || "embedded".equalsIgnoreCase(forkMode)) { |
| fork = false; |
| |
| try { |
| initEmbeddedLauncher(); |
| } catch (Exception e) { |
| fork = true; |
| } |
| } else { |
| fork = true; |
| } |
| |
| if (!fork) { |
| if (!envVars.isEmpty()) { |
| throw new LauncherException("Environment variables are not supported in embedded runtime"); |
| } |
| |
| initEmbeddedLauncher(); |
| |
| return embeddedLauncher; |
| } else { |
| return new ForkedLauncher(mavenHome, envVars, debugJvm, useWrapper, mvnExecutable); |
| } |
| } |
| |
| private void initEmbeddedLauncher() throws LauncherException { |
| if (embeddedLauncher == null) { |
| if (mavenHome == null || mavenHome.isEmpty()) { |
| embeddedLauncher = Embedded3xLauncher.createFromClasspath(); |
| } else { |
| String defaultClasspath = System.getProperty("maven.bootclasspath"); |
| String defaultClassworldConf = System.getProperty("classworlds.conf"); |
| embeddedLauncher = Embedded3xLauncher.createFromMavenHome( |
| mavenHome, defaultClassworldConf, parseClasspath(defaultClasspath)); |
| } |
| } |
| } |
| |
| private static List<URL> parseClasspath(String classpath) throws LauncherException { |
| if (classpath == null) { |
| return null; |
| } |
| ArrayList<URL> classpathUrls = new ArrayList<>(); |
| StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator); |
| while (st.hasMoreTokens()) { |
| try { |
| classpathUrls.add(new File(st.nextToken()).toURI().toURL()); |
| } catch (MalformedURLException e) { |
| throw new LauncherException("Invalid launcher classpath " + classpath, e); |
| } |
| } |
| return classpathUrls; |
| } |
| |
| public String getMavenVersion() throws VerificationException { |
| try { |
| return getMavenLauncher(Collections.emptyMap()).getMavenVersion(); |
| } catch (LauncherException | IOException e) { |
| throw new VerificationException(e); |
| } |
| } |
| |
| private static String getLogContents(File logFile) { |
| try { |
| return FileUtils.fileRead(logFile); |
| } catch (IOException e) { |
| // ignore |
| return "(Error reading log contents: " + e.getMessage() + ")"; |
| } |
| } |
| |
| private void findLocalRepo(String settingsFile) throws VerificationException { |
| if (localRepo == null) { |
| // Settings file is the primary configuration source — consult it first. |
| try { |
| localRepo = retrieveLocalRepo(settingsFile); |
| } catch (SettingsBuildingException e) { |
| throw new VerificationException("Cannot read settings.xml to determine local repository location", e); |
| } |
| } |
| |
| if (localRepo == null) { |
| // System property is the last resort before the hardcoded default. |
| // It is intentionally checked AFTER the settings file because |
| // Embedded3xLauncher.run() calls System.setProperties(null) during embedded |
| // Maven executions, creating a race window where this property may transiently |
| // hold a value injected by a concurrent Maven invocation running in the same JVM. |
| // Prefer Builder.localRepository() for an entirely race-free alternative. |
| localRepo = System.getProperty("maven.repo.local"); |
| } |
| |
| if (localRepo == null) { |
| localRepo = USER_HOME + "/.m2/repository"; |
| } |
| |
| File repoDir = new File(localRepo); |
| |
| if (!repoDir.exists()) { |
| //noinspection ResultOfMethodCallIgnored |
| repoDir.mkdirs(); |
| } |
| |
| // normalize path |
| localRepo = repoDir.getAbsolutePath(); |
| |
| localRepoLayout = System.getProperty("maven.repo.local.layout", "default"); |
| } |
| |
| /** |
| * Verifies that the artifact given by its Maven coordinates exists and contains the given content. |
| * |
| * @param groupId the groupId of the artifact (must not be null) |
| * @param artifactId the artifactId of the artifact (must not be null) |
| * @param version the version of the artifact (must not be null) |
| * @param ext the extension of the artifact (must not be null) |
| * @param content the expected content |
| * @throws IOException if reading from the artifact fails |
| * @throws VerificationException if the content of the artifact differs |
| */ |
| public void verifyArtifactContent(String groupId, String artifactId, String version, String ext, String content) |
| throws IOException, VerificationException { |
| String fileName = getArtifactPath(groupId, artifactId, version, ext); |
| if (!content.equals(FileUtils.fileRead(fileName))) { |
| throw new VerificationException("Content of " + fileName + " does not equal " + content); |
| } |
| } |
| |
| static class UserModelReader extends DefaultHandler { |
| private String localRepository; |
| |
| private StringBuilder currentBody = new StringBuilder(); |
| |
| public void parse(File file) throws VerificationException { |
| try { |
| SAXParserFactory saxFactory = SAXParserFactory.newInstance(); |
| |
| SAXParser parser = saxFactory.newSAXParser(); |
| |
| InputSource is = new InputSource(new FileInputStream(file)); |
| |
| parser.parse(is, this); |
| } catch (FileNotFoundException e) { |
| throw new VerificationException("file not found path : " + file.getAbsolutePath(), e); |
| } catch (IOException e) { |
| throw new VerificationException(" IOException path : " + file.getAbsolutePath(), e); |
| } catch (ParserConfigurationException e) { |
| throw new VerificationException(e); |
| } catch (SAXException e) { |
| throw new VerificationException("Parsing exception for file " + file.getAbsolutePath(), e); |
| } |
| } |
| |
| public void warning(SAXParseException spe) { |
| // ignore warnings |
| } |
| |
| public void error(SAXParseException spe) throws SAXException { |
| throw new SAXException(spe); |
| } |
| |
| public void fatalError(SAXParseException spe) throws SAXException { |
| throw new SAXException(spe); |
| } |
| |
| public String getLocalRepository() { |
| return localRepository; |
| } |
| |
| public void characters(char[] ch, int start, int length) throws SAXException { |
| currentBody.append(ch, start, length); |
| } |
| |
| public void endElement(String uri, String localName, String rawName) throws SAXException { |
| if ("localRepository".equals(rawName)) { |
| if (notEmpty(currentBody.toString())) { |
| localRepository = currentBody.toString().trim(); |
| } else { |
| throw new SAXException( |
| "Invalid mavenProfile entry. Missing one or more " + "fields: {localRepository}."); |
| } |
| } |
| |
| currentBody = new StringBuilder(); |
| } |
| |
| private boolean notEmpty(String test) { |
| return test != null && test.trim().length() > 0; |
| } |
| |
| public void reset() { |
| currentBody = null; |
| localRepository = null; |
| } |
| } |
| |
| /** |
| * @deprecated will be removed without replacement, |
| * for arguments adding please use {@link #addCliArgument(String)}, {@link #addCliArguments(String...)} |
| */ |
| @Deprecated |
| public List<String> getCliOptions() { |
| return cliArguments; |
| } |
| |
| /** |
| * @deprecated will be removed |
| */ |
| @Deprecated |
| public void setCliOptions(List<String> cliOptions) { |
| this.cliArguments = cliOptions; |
| } |
| |
| /** |
| * Add a command line argument, each argument must be set separately one by one. |
| * <p> |
| * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution. |
| * @param option an argument to add |
| * @deprecated please use {@link #addCliArgument(String)} |
| */ |
| @Deprecated |
| public void addCliOption(String option) { |
| addCliArgument(option); |
| } |
| |
| /** |
| * Add a command line argument, each argument must be set separately one by one. |
| * <p> |
| * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution. |
| * |
| * @param cliArgument an argument to add |
| */ |
| public void addCliArgument(String cliArgument) { |
| cliArguments.add(cliArgument); |
| } |
| |
| /** |
| * Add a command line arguments, each argument must be set separately one by one. |
| * <p> |
| * <code>${basedir}</code> in argument will be replaced by value of {@link #getBasedir()} during execution. |
| * |
| * @param cliArguments an arguments list to add |
| */ |
| public void addCliArguments(String... cliArguments) { |
| Collections.addAll(this.cliArguments, cliArguments); |
| } |
| |
| public Properties getSystemProperties() { |
| return systemProperties; |
| } |
| |
| public void setSystemProperties(Properties systemProperties) { |
| this.systemProperties = systemProperties; |
| } |
| |
| public void setSystemProperty(String key, String value) { |
| if (value != null) { |
| systemProperties.setProperty(key, value); |
| } else { |
| systemProperties.remove(key); |
| } |
| } |
| |
| public Map<String, String> getEnvironmentVariables() { |
| return environmentVariables; |
| } |
| |
| public void setEnvironmentVariables(Map<String, String> environmentVariables) { |
| this.environmentVariables = environmentVariables; |
| } |
| |
| public void setEnvironmentVariable(String key, String value) { |
| if (value != null) { |
| environmentVariables.put(key, value); |
| } else { |
| environmentVariables.remove(key); |
| } |
| } |
| |
| public Properties getVerifierProperties() { |
| return verifierProperties; |
| } |
| |
| public void setVerifierProperties(Properties verifierProperties) { |
| this.verifierProperties = verifierProperties; |
| } |
| |
| public boolean isAutoclean() { |
| return autoclean; |
| } |
| |
| /** |
| * Clean project before execution by adding {@link #CLEAN_CLI_ARGUMENT} to command line. |
| * <p> |
| * By default, options is enabled. |
| * |
| * @param autoclean indicate if option is enabled |
| */ |
| public void setAutoclean(boolean autoclean) { |
| this.autoclean = autoclean; |
| } |
| |
| public String getBasedir() { |
| return basedir; |
| } |
| |
| /** |
| * Gets the name of the file used to log build output. |
| * |
| * @return The name of the log file, relative to the base directory, never <code>null</code>. |
| * @since 1.2 |
| */ |
| public String getLogFileName() { |
| return this.logFileName; |
| } |
| |
| /** |
| * Sets the name of the file used to log build output. |
| * |
| * @param logFileName The name of the log file, relative to the base directory, must not be empty or |
| * <code>null</code>. |
| * @since 1.2 |
| */ |
| public void setLogFileName(String logFileName) { |
| if (logFileName == null || logFileName.isEmpty()) { |
| throw new IllegalArgumentException("log file name unspecified"); |
| } |
| this.logFileName = logFileName; |
| } |
| |
| /** |
| * |
| * @param logFile configure the log file used to log build output |
| * @since 2.0 ish (not really sure if this will be this) |
| */ |
| public void setLogFile(File logFile) { |
| this.logFile = Objects.requireNonNull(logFile, "log file cannot be null"); |
| } |
| |
| /** |
| * |
| * @return The log file used to log build output |
| * @since 2.0 ish (not really sure if this will be this) |
| */ |
| public File getLogFile() { |
| return logFile == null ? new File(getBasedir(), logFileName) : logFile; |
| } |
| |
| /** |
| * @deprecated will be removed without replacement |
| */ |
| @Deprecated |
| public void setDebug(boolean debug) {} |
| |
| /** |
| * @deprecated will be removed without replacement |
| */ |
| @Deprecated |
| public boolean isMavenDebug() { |
| return mavenDebug; |
| } |
| |
| /** |
| * For replacement please use: |
| * <pre> |
| * verifier.addCliArgument( "-X" ); |
| * </pre> |
| * |
| * @deprecated will be removed without replacement. |
| */ |
| @Deprecated |
| public void setMavenDebug(boolean mavenDebug) { |
| this.mavenDebug = mavenDebug; |
| } |
| |
| public void setForkJvm(boolean forkJvm) { |
| this.forkJvm = forkJvm; |
| } |
| |
| public boolean isDebugJvm() { |
| return debugJvm; |
| } |
| |
| public void setDebugJvm(boolean debugJvm) { |
| this.debugJvm = debugJvm; |
| } |
| |
| public String getLocalRepoLayout() { |
| return localRepoLayout; |
| } |
| |
| public void setLocalRepoLayout(String localRepoLayout) { |
| this.localRepoLayout = localRepoLayout; |
| } |
| |
| public String getLocalRepository() { |
| return localRepo; |
| } |
| |
| public void setMavenHome(String mavenHome) { |
| this.mavenHome = mavenHome; |
| setUseWrapper(false); |
| setForkMode(); |
| } |
| |
| public void setForkMode(String forkMode) { |
| this.forkMode = forkMode; |
| setForkMode(); |
| } |
| |
| public void setUseWrapper(boolean useWrapper) { |
| this.useWrapper = useWrapper; |
| } |
| |
| public void setSettingsFile(String settingsFile) throws VerificationException { |
| this.settingsFile = settingsFile; |
| findLocalRepo(settingsFile); |
| } |
| |
| public void setDefaultCliArguments(String[] defaultCliArguments) { |
| this.defaultCliArguments = defaultCliArguments == null ? new String[0] : defaultCliArguments.clone(); |
| } |
| |
| private void setForkMode() { |
| if ((mavenHome == null || mavenHome.isEmpty()) && (forkMode == null || forkMode.isEmpty())) { |
| forkMode = "auto"; |
| } |
| } |
| |
| /** |
| * Builder for creating {@link Verifier} instances with explicit configuration. |
| * <p> |
| * When {@link #localRepository(String)} is set, the local repository is pre-populated |
| * before {@code findLocalRepo()} runs, so the system property |
| * {@code maven.repo.local} is never consulted. This eliminates the race condition that |
| * occurs when multiple {@code Verifier} instances are constructed concurrently in the |
| * same JVM while {@link Embedded3xLauncher#run} is manipulating the JVM-global system |
| * properties table ({@code System.setProperties(null)}). |
| */ |
| public static class Builder { |
| private final String basedir; |
| private String settingsFile; |
| private String localRepository; |
| private String forkMode; |
| private Boolean forkJvm; |
| private String mvnExecutable; |
| private String[] defaultCliArguments; |
| |
| public Builder(String basedir) { |
| this.basedir = basedir; |
| } |
| |
| public Builder settingsFile(String settingsFile) { |
| this.settingsFile = settingsFile; |
| return this; |
| } |
| |
| /** |
| * Sets the local repository path explicitly. |
| * When provided, the system property {@code maven.repo.local} and the settings file |
| * are both bypassed for local repository resolution, making construction race-free. |
| */ |
| public Builder localRepository(String localRepository) { |
| this.localRepository = localRepository; |
| return this; |
| } |
| |
| /** |
| * Sets the forkMode explicitly. |
| * When provided, the system property verifier.forkMode is bypassed. |
| */ |
| public Builder forkMode(String forkMode) { |
| this.forkMode = forkMode; |
| return this; |
| } |
| |
| /** |
| * Sets the forkJvm explicitly. |
| */ |
| public Builder forkJvm(Boolean forkJvm) { |
| this.forkJvm = forkJvm; |
| return this; |
| } |
| |
| /** |
| * Sets the maven full path executable explicitly. |
| * When provided, the maven executable is used directly without any lookup or resolution. |
| */ |
| public Builder mvnExecutable(String mvnExecutable) { |
| this.mvnExecutable = mvnExecutable; |
| return this; |
| } |
| |
| /** |
| * Sets the default CLI arguments to be used for every execution |
| */ |
| public Builder defaultCliArguments(String[] defaultCliArguments) { |
| this.defaultCliArguments = defaultCliArguments; |
| return this; |
| } |
| |
| public Verifier build() throws VerificationException { |
| return new Verifier(this); |
| } |
| } |
| |
| private Verifier(Builder builder) throws VerificationException { |
| this.basedir = builder.basedir; |
| this.forkMode = builder.forkMode == null ? System.getProperty("verifier.forkMode") : builder.forkMode; |
| setForkMode(); |
| this.forkJvm = builder.forkJvm; |
| // Pre-set localRepo before findLocalRepo() so that the settings-file and |
| // system-property lookups in findLocalRepo() are both skipped when an |
| // explicit value has been provided via the builder. |
| this.localRepo = builder.localRepository; |
| if (builder.settingsFile != null) { |
| this.settingsFile = builder.settingsFile; |
| } |
| findLocalRepo(this.settingsFile); |
| this.mavenHome = System.getProperty("maven.home"); |
| this.useWrapper = Files.exists(Paths.get(getBasedir(), "mvnw")); |
| this.defaultCliArguments = |
| builder.defaultCliArguments == null ? DEFAULT_CLI_ARGUMENTS.clone() : builder.defaultCliArguments; |
| this.mvnExecutable = builder.mvnExecutable; |
| } |
| } |