MavenLocator.java

  1. package com.soebes.itf.jupiter.extension;

  2. /*
  3.  * Licensed to the Apache Software Foundation (ASF) under one
  4.  * or more contributor license agreements.  See the NOTICE file
  5.  * distributed with this work for additional information
  6.  * regarding copyright ownership.  The ASF licenses this file
  7.  * to you under the Apache License, Version 2.0 (the
  8.  * "License"); you may not use this file except in compliance
  9.  * with the License.  You may obtain a copy of the License at
  10.  *
  11.  *  http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing,
  14.  * software distributed under the License is distributed on an
  15.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16.  * KIND, either express or implied.  See the License for the
  17.  * specific language governing permissions and limitations
  18.  * under the License.
  19.  */

  20. import java.nio.file.FileSystem;
  21. import java.nio.file.Files;
  22. import java.nio.file.Path;
  23. import java.util.Optional;
  24. import java.util.regex.Pattern;

  25. /**
  26.  * It's the intention to find the {@code mvn} executable.
  27.  * <p>
  28.  * The {@code maven.home} is set during the execution of the
  29.  * test by the user.
  30.  * The {@code maven.home} has precedence over searching
  31.  * of Maven via {@code PATH}. This gives the opportunity
  32.  * to overload the given Maven version in cases where needed.
  33.  *
  34.  * @author Karl Heinz Marbaise
  35.  * @implNote Currently {@code maven.home} is given to the maven-failsafe-plugin
  36.  * configuration as a system property.
  37.  * The parameter {@code isRunningOnWindows} is used instead
  38.  * of using things like {@code org.junit.jupiter.api.condition.OS.WINDOWS.isCurrentOs()} because
  39.  * they contain static initializers etc. which can't be tested.
  40.  */
  41. class MavenLocator {

  42.   /**
  43.    * The name of the system property.
  44.    */
  45.   private static final String MAVEN_HOME = "maven.home";

  46.   /**
  47.    * The name of the system property to activate remote debugging.
  48.    */
  49.   private static final String ITF_DEBUG = "ITF_DEBUG";


  50.   private final FileSystem fileSystem;
  51.   private final Optional<String> pathEnvironment;
  52.   private final boolean isRunningOnWindows;
  53.   private final String  mvnExecutable;

  54.   /**
  55.    * @param fileSystem The {@link FileSystem} which is used.
  56.    * @param pathEnvironment The value of {@code PATH} if it exists otherwise {@link Optional#empty()}.
  57.    * @param isRunningOnWindows {@code true} when running on Windows, false otherwise.
  58.    */
  59.   MavenLocator(FileSystem fileSystem, Optional<String> pathEnvironment, boolean isRunningOnWindows) {
  60.     this.fileSystem = fileSystem;
  61.     this.pathEnvironment = pathEnvironment;
  62.     this.isRunningOnWindows = isRunningOnWindows;
  63.     this.mvnExecutable = Boolean.getBoolean(ITF_DEBUG) ? "mvnDebug" : "mvn";
  64.   }

  65.   private Path intoPath(String s) {
  66.     return this.fileSystem.getPath(s);
  67.   }

  68.   private Path intoBin(Path p) {
  69.     return p.resolve("bin");
  70.   }

  71.   private Optional<String> mavenHomeFromSystemProperty() {
  72.     return Optional.ofNullable(System.getProperty(MAVEN_HOME));
  73.   }

  74.   private Path toMvn(Path p) {
  75.     return p.resolve(this.mvnExecutable);
  76.   }

  77.   private Path toBat(Path p) {
  78.     return p.resolve(this.mvnExecutable + ".bat");
  79.   }

  80.   private Path toCmd(Path p) {
  81.     return p.resolve(this.mvnExecutable + ".cmd");
  82.   }

  83.   private boolean isExecutable(Path s) {
  84.     return Files.isRegularFile(s)
  85.         && Files.isReadable(s)
  86.         && Files.isExecutable(s);
  87.   }

  88.   private Optional<Path> executableNonWindows(Path s) {
  89.     return Optional.of(toMvn(s))
  90.         .filter(this::isExecutable);
  91.   }

  92.   private Optional<Path> executableWindows(Path s) {
  93.     Path mvnBat = toBat(s);
  94.     //Maven 3.0.5...3.2.5
  95.     if (isExecutable(mvnBat)) {
  96.       return Optional.of(mvnBat);
  97.     }

  98.     //Maven 3.3.1...
  99.     Path mvnCmd = toCmd(s);
  100.     if (isExecutable(mvnCmd)) {
  101.       return Optional.of(mvnCmd);
  102.     }
  103.     return Optional.empty();
  104.   }

  105.   private Optional<Path> executable(Path p) {
  106.     if (this.isRunningOnWindows) {
  107.       return executableWindows(p);
  108.     } else {
  109.       return executableNonWindows(p);
  110.     }
  111.   }

  112.   private Optional<Path> checkExecutableViaPathEnvironment() {
  113.     if (!this.pathEnvironment.isPresent()) {
  114.       return Optional.empty();
  115.     }

  116.     String pathSeparator = this.isRunningOnWindows ? ";" : ":";
  117.     Pattern pathSeparatorPattern = Pattern.compile(Pattern.quote(pathSeparator));
  118.     return pathSeparatorPattern.splitAsStream(pathEnvironment.get())
  119.         .map(this::intoPath)
  120.         .map(this::executable)
  121.         .filter(Optional::isPresent)
  122.         .findFirst()
  123.         .orElse(Optional.empty());
  124.   }

  125.   Optional<Path> findMvn() {
  126.     Optional<Path> path = mavenHomeFromSystemProperty()
  127.         .map(this::intoPath)
  128.         .map(this::intoBin)
  129.         .flatMap(this::executable);
  130.     if (path.isPresent()) {
  131.       return path;
  132.     }
  133.     return checkExecutableViaPathEnvironment();
  134.   }
  135. }