1 /*
   2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package jdk.incubator.jpackage.internal;
  26 
  27 import java.io.IOException;
  28 import java.nio.file.Path;
  29 import java.text.MessageFormat;
  30 import java.util.ArrayList;
  31 import java.util.List;
  32 import java.util.concurrent.atomic.AtomicBoolean;
  33 import java.util.function.BiFunction;
  34 import java.util.function.Function;
  35 import java.util.stream.Stream;
  36 
  37 
  38 public final class ToolValidator {
  39 
  40     ToolValidator(String tool) {
  41         this(Path.of(tool));
  42     }
  43 
  44     ToolValidator(Path toolPath) {
  45         this.toolPath = toolPath;
  46         args = new ArrayList<>();
  47 
  48         if (Platform.getPlatform() == Platform.LINUX) {
  49             setCommandLine("--version");
  50         }
  51 
  52         setToolNotFoundErrorHandler(null);
  53         setToolOldVersionErrorHandler(null);
  54     }
  55 
  56     ToolValidator setCommandLine(String... args) {
  57         this.args = List.of(args);
  58         return this;
  59     }
  60 
  61     ToolValidator setMinimalVersion(Comparable<String> v) {
  62         minimalVersion = v;
  63         return this;
  64     }
  65 
  66     ToolValidator setVersionParser(Function<Stream<String>, String> v) {
  67         versionParser = v;
  68         return this;
  69     }
  70 
  71     ToolValidator setToolNotFoundErrorHandler(
  72             BiFunction<String, IOException, ConfigException> v) {
  73         toolNotFoundErrorHandler = v;
  74         return this;
  75     }
  76 
  77     ToolValidator setToolOldVersionErrorHandler(BiFunction<String, String, ConfigException> v) {
  78         toolOldVersionErrorHandler = v;
  79         return this;
  80     }
  81 
  82     ConfigException validate() {
  83         List<String> cmdline = new ArrayList<>();
  84         cmdline.add(toolPath.toString());
  85         cmdline.addAll(args);
  86 
  87         String name = toolPath.getFileName().toString();
  88         try {
  89             ProcessBuilder pb = new ProcessBuilder(cmdline);
  90             AtomicBoolean canUseTool = new AtomicBoolean();
  91             if (minimalVersion == null) {
  92                 // No version check.
  93                 canUseTool.setPlain(true);
  94             }
  95 
  96             String[] version = new String[1];
  97             Executor.of(pb).setOutputConsumer(lines -> {
  98                 if (versionParser != null && minimalVersion != null) {
  99                     version[0] = versionParser.apply(lines);
 100                     if (minimalVersion.compareTo(version[0]) < 0) {
 101                         canUseTool.setPlain(true);
 102                     }
 103                 }
 104             }).execute();
 105 
 106             if (!canUseTool.getPlain()) {
 107                 if (toolOldVersionErrorHandler != null) {
 108                     return toolOldVersionErrorHandler.apply(name, version[0]);
 109                 }
 110                 return new ConfigException(MessageFormat.format(I18N.getString(
 111                         "error.tool-old-version"), name, minimalVersion),
 112                         MessageFormat.format(I18N.getString(
 113                                 "error.tool-old-version.advice"), name,
 114                                 minimalVersion));
 115             }
 116         } catch (IOException e) {
 117             if (toolNotFoundErrorHandler != null) {
 118                 return toolNotFoundErrorHandler.apply(name, e);
 119             }
 120             return new ConfigException(MessageFormat.format(I18N.getString(
 121                     "error.tool-not-found"), name, e.getMessage()),
 122                     MessageFormat.format(I18N.getString(
 123                             "error.tool-not-found.advice"), name), e);
 124         }
 125 
 126         // All good. Tool can be used.
 127         return null;
 128     }
 129 
 130     private final Path toolPath;
 131     private List<String> args;
 132     private Comparable<String> minimalVersion;
 133     private Function<Stream<String>, String> versionParser;
 134     private BiFunction<String, IOException, ConfigException> toolNotFoundErrorHandler;
 135     private BiFunction<String, String, ConfigException> toolOldVersionErrorHandler;
 136 }