1 /*
   2  * Copyright (c) 2013, 2016, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.testlibrary.tasks;
  25 
  26 import java.util.ArrayList;
  27 import java.util.Arrays;
  28 import static java.util.Arrays.asList;
  29 import static java.util.Collections.EMPTY_LIST;
  30 import java.util.List;
  31 import java.util.Map;
  32 import java.util.function.Function;
  33 import java.util.stream.Collectors;
  34 import static java.util.stream.Collectors.toList;
  35 import java.util.stream.Stream;
  36 
  37 /**
  38  * A task to configure and run the Java launcher.
  39  */
  40 public class JavaTask extends AbstractTask<JavaTask> {
  41     private String classpath;
  42     private String modulepath;
  43     private String className;
  44     private String moduleName;
  45     private List<String> vmOptions;
  46     private List<String> classArgs;
  47     private List<String> addExports;
  48     private List<String> addModules;
  49     private List<String> standardOptions;
  50 
  51     /**
  52      * Create a task to run the Java launcher, using {@code EXEC} mode.
  53      */
  54     public JavaTask() {
  55         super(Task.Mode.EXEC);
  56         standardOptions = Stream.of("test.vm.opts", "test.java.opts")
  57                 .map(p -> System.getProperty(p))
  58                 .filter(p -> p != null && !p.isEmpty())
  59                 .map(o -> asList(o.split(" +")))
  60                 .flatMap(l -> l.stream()).collect(toList());
  61     }
  62 
  63     /**
  64      * Sets the classpath.
  65      * @param classpath the classpath
  66      * @return this task object
  67      */
  68     public JavaTask classpath(String classpath) {
  69         this.classpath = classpath;
  70         return this;
  71     }
  72 
  73     /**
  74      * Sets the modulepath.
  75      * @param modulepath the modulepath
  76      * @return this task object
  77      */
  78     public JavaTask modulepath(String modulepath) {
  79         this.modulepath = modulepath;
  80         return this;
  81     }
  82 
  83     /**
  84      * Sets the {@code --add-exports} option.
  85      * @param addExports value for the option
  86      * @return this task object
  87      */
  88     public JavaTask addExports(String... addExports) {
  89         this.addExports = Arrays.asList(addExports);
  90         return this;
  91     }
  92 
  93     /**
  94      * Sets the {@code --add-modules} option.
  95      * @param addModules value for the option
  96      * @return this task object
  97      */
  98     public JavaTask addModules(String... addModules) {
  99         this.addModules = Arrays.asList(addModules);
 100         return this;
 101     }
 102 
 103     /**
 104      * Sets the VM options.
 105      * @param vmOptions the options
 106      * @return this task object
 107      */
 108     public JavaTask vmOptions(String... vmOptions) {
 109         this.vmOptions = Arrays.asList(vmOptions);
 110         return this;
 111     }
 112 
 113     /**
 114      * Sets the name of the class to be executed.
 115      * @param className the name of the class
 116      * @return this task object
 117      */
 118     public JavaTask className(String className) {
 119         this.className = className;
 120         return this;
 121     }
 122 
 123     /**
 124      * Sets the name of the module which contains class to be executed. If empty,
 125      * the main class is assumed to be in the unnamed module.
 126      * @param moduleName the name of the module
 127      * @return this task object
 128      */
 129     public JavaTask moduleName(String moduleName) {
 130         this.moduleName = moduleName;
 131         return this;
 132     }
 133 
 134     /**
 135      * Sets the arguments for the class to be executed.
 136      * @param classArgs the arguments
 137      * @return this task object
 138      */
 139     public JavaTask classArgs(String... classArgs) {
 140         this.classArgs = Arrays.asList(classArgs);
 141         return this;
 142     }
 143 
 144     /**
 145      * Sets that the standard VM and java options would not be passed
 146      * to the new VM instance. If this method is not called, the default behavior 
 147      * is that the options will be passed to the new VM instance.
 148      *
 149      * @return this task object
 150      */
 151     public JavaTask ignoreStandardOptions() {
 152         standardOptions = EMPTY_LIST;
 153         return this;
 154     }
 155     
 156     /**
 157      * Filters the standard VM and java options.
 158      * @param filter a filter
 159      * @return 
 160      */
 161     public JavaTask filterStandardOption(Function<List<String>, List<String>> filter) {
 162         standardOptions = filter.apply(standardOptions);
 163         return this;
 164     }
 165 
 166     /**
 167      * Filters the standard VM and java options by taking away a particular option 
 168      * and a specified number or parameters after it. 
 169      *
 170      * @param option the option, such as {@code --limit-modules}
 171      * @param valueCount number of parameters to remove after the option.
 172      * @return this task object
 173      */
 174     public JavaTask ignoreStandardOption(String option, int valueCount) {
 175         return filterStandardOption(os -> {
 176             List<String> filtered = new ArrayList<>();
 177             for(int i = 0; i < os.size(); i++) {
 178                 if(os.get(i).equals(option))
 179                     i += valueCount;
 180                 else
 181                     filtered.add(os.get(i));
 182             }
 183             return filtered;
 184         });
 185     }
 186 
 187     /**
 188      * Filters the standard VM and java options so that there are no options which 
 189      * affects a set of resolvable modules or a set of accessible APIs. 
 190      *
 191      * @return this task object
 192      */
 193     public JavaTask ignoreStandardModuleOptions() {
 194         moduleOptions.entrySet().stream()
 195                 .forEach(e -> ignoreStandardOption(e.getKey(), e.getValue()));
 196         return this;
 197     }
 198     
 199     /**
 200      * {@inheritDoc}
 201      * @return the name "java"
 202      */
 203     @Override
 204     public String name() {
 205         return "java";
 206     }
 207 
 208     /**
 209      * Calls the Java launcher with the arguments as currently configured.
 210      * @return a Result object indicating the outcome of the task
 211      * and the content of any output written to stdout or stderr.
 212      * @throws TaskError if the outcome of the task is not as expected.
 213      */
 214     @Override
 215     public Task.Result run() {
 216         List<String> args = new ArrayList<>(standardOptions);
 217         if (classpath != null) {
 218             args.add("--class-path");
 219             args.add(classpath);
 220         }
 221         if (modulepath != null) {
 222             args.add("--module-path");
 223             args.add(modulepath);
 224         }
 225         if (addExports != null) {
 226             args.add("--add-exports");
 227             args.add(addExports.stream().collect(Collectors.joining(",")));
 228         }
 229         if (addModules != null) {
 230             args.add("--add-modules");
 231             args.add(addModules.stream().collect(Collectors.joining(",")));
 232         }
 233         if (vmOptions != null)
 234             args.addAll(vmOptions);
 235         if (className != null)
 236             if (moduleName != null) {
 237                 args.add("-m");
 238                 args.add(moduleName + "/" + className);
 239             } else
 240                 args.add(className);
 241         if (classArgs != null)
 242             args.addAll(classArgs);
 243         return run(Tool.JAVA, args);
 244     }
 245 
 246     //this is a list of options to ignore in ignoreStandardModuleOptions()
 247     private static final Map<String, Integer> moduleOptions = 
 248             Map.of("--limit-modules", 1,
 249                     "--upgrade-module-path", 1,
 250                     "--add-modules", 1,
 251                     "--add-reads", 1,
 252                     "--add-exports", 1,
 253                     "--patch-module", 1);
 254 }