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.nio.file.Path; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import static java.util.Arrays.asList; 30 import static java.util.Arrays.stream; 31 import static java.util.Collections.EMPTY_LIST; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.function.Function; 35 import static java.util.stream.Collectors.joining; 36 import static java.util.stream.Collectors.toList; 37 import java.util.stream.Stream; 38 39 /** 40 * A task to configure and run the Java launcher. 41 */ 42 public class JavaTask extends AbstractTask<JavaTask> { 43 /** 44 * {@code --class-path} 45 */ 46 public static final String CLASS_PATH = "--class-path"; 47 /** 48 * {@code --module-path} 49 */ 50 public static final String MODULE_PATH = "--module-path"; 51 /** 52 * {@code --upgrade-module-path} 53 */ 54 public static final String UPGRADE_MODULE_PATH = "--upgrade-module-path"; 55 /** 56 * {@code --add-modules} 57 */ 58 public static final String ADD_MODULES = "--add-modules"; 59 /** 60 * {@code --limit-modules} 61 */ 62 public static final String LIMIT_MODULES = "--limit-modules"; 63 /** 64 * {@code --add-exports} 65 */ 66 public static final String ADD_EXPORTS = "--add-exports"; 67 /** 68 * {@code --add-reads} 69 */ 70 public static final String ADD_READS = "--add-reads"; 71 /** 72 * {@code --patch-module} 73 */ 74 public static final String PATCH_MODULE = "--patch-module"; 75 76 private List<String> classPath; 77 private List<String> modulePath; 78 private List<String> upgradeModulePath; 79 private String mainClass; 80 private String module; 81 private List<String> vmOptions; 82 private List<String> classArgs; 83 private List<String> limitModules; 84 private List<String> addModules; 85 private List<String> standardOptions; 86 private List<String> addExports = new ArrayList<>(); 87 private List<String> addReads = new ArrayList<>(); 88 private List<String> patchModule = new ArrayList<>(); 89 90 /** 91 * Create a task to run the Java launcher, using {@code EXEC} mode. 92 */ 93 public JavaTask() { 94 super(Task.Mode.EXEC); 95 standardOptions = Stream.of("test.vm.opts", "test.java.opts") 96 .map(p -> System.getProperty(p)) 97 .filter(p -> p != null && !p.isEmpty()) 98 .map(o -> asList(o.split(" +"))) 99 .flatMap(l -> l.stream()).collect(toList()); 100 } 101 102 /** 103 * Sets the classpath. 104 * @param classPath the classpath 105 * @return this task object 106 */ 107 public JavaTask classPath(String... classPath) { 108 this.classPath = stream(classPath).collect(toList()); 109 return this; 110 } 111 112 /** 113 * Sets the classpath. 114 * @param classPath the classpath 115 * @return this task object 116 */ 117 public JavaTask classPath(Path... classPath) { 118 this.classPath = stream(classPath) 119 .map(Object::toString).collect(toList()); 120 return this; 121 } 122 123 /** 124 * Sets the module path. 125 * @param modulePath the module path 126 * @return this task object 127 */ 128 public JavaTask modulePath(String... modulePath) { 129 this.modulePath = stream(modulePath).collect(toList()); 130 return this; 131 } 132 133 /** 134 * Sets the module path. 135 * @param modulePath the module path 136 * @return this task object 137 */ 138 public JavaTask modulePath(Path... modulePath) { 139 this.modulePath = stream(modulePath) 140 .map(Object::toString).collect(toList()); 141 return this; 142 } 143 144 /** 145 * Sets the upgrade module path. 146 * @param upgradeModulePath the upgrade module path 147 * @return this task object 148 */ 149 public JavaTask upgradeModulePath(String... upgradeModulePath) { 150 this.upgradeModulePath = stream(upgradeModulePath).collect(toList()); 151 return this; 152 } 153 154 /** 155 * Sets the upgrade module path. 156 * @param upgradeModulePath the upgrade module path 157 * @return this task object 158 */ 159 public JavaTask upgradeModulePath(Path... upgradeModulePath) { 160 this.upgradeModulePath = stream(upgradeModulePath) 161 .map(Object::toString).collect(toList()); 162 return this; 163 } 164 165 /** 166 * Adds single occurrence of the {@code --add-exports} option. 167 * @param source source module 168 * @param pkg exported package 169 * @param targets target modules. If empty, {@code ALL-UNNAMED} is used as 170 * the target. 171 * @return this task object 172 */ 173 public JavaTask addExports(String source, String pkg, String... targets) { 174 String targetList; 175 if(targets.length == 0) { 176 targetList = ALL_UNNAMED; 177 } else { 178 targetList = stream(targets).collect(joining(",")); 179 } 180 this.addExports.add(source + "/" + pkg + "=" + targetList); 181 return this; 182 } 183 184 /** 185 * Sets the {@code --add-modules} option. 186 * @param addModules parameter for the option 187 * @return this task object 188 */ 189 public JavaTask addModules(String... addModules) { 190 this.addModules = asList(addModules); 191 return this; 192 } 193 194 /** 195 * Adds single occurrence of the {@code --add-reads} option. 196 * @param source source module 197 * @param targets target modules. If empty, {@code ALL-UNNAMED} is used as 198 * the target. 199 * @return this task object 200 */ 201 public JavaTask addReads(String source, String... targets) { 202 String targetList; 203 if(targets.length == 0) { 204 targetList = ALL_UNNAMED; 205 } else { 206 targetList = stream(targets).collect(joining(",")); 207 } 208 this.addReads.add(source + "=" + targetList); 209 return this; 210 } 211 212 /** 213 * Adds single occurrence of the {@code --patch-module} option. 214 * @param module module to be overridden or augmented 215 * @param files jar files and directories 216 * @return this task object 217 */ 218 public JavaTask patchModule(String module, String... files) { 219 patchModule(module, stream(files)); 220 return this; 221 } 222 223 /** 224 * Adds single occurrence of the {@code --patch-module} option. 225 * @param module module to be overridden or augmented 226 * @param files jar files and directories 227 * @return this task object 228 */ 229 public JavaTask patchModule(String module, Path... files) { 230 patchModule(module, stream(files).map(Object::toString)); 231 return this; 232 } 233 234 /** 235 * Sets the {@code --limit-modules} option. 236 * @param limitModules parameter for the option 237 * @return this task object 238 */ 239 public JavaTask limitModules(String... limitModules) { 240 this.limitModules = asList(limitModules); 241 return this; 242 } 243 244 /** 245 * Sets the VM options. 246 * @param vmOptions the options 247 * @return this task object 248 */ 249 public JavaTask vmOptions(String... vmOptions) { 250 this.vmOptions = asList(vmOptions); 251 return this; 252 } 253 254 /** 255 * Sets the name of the class to be executed. 256 * @param mainClass the name of the class 257 * @return this task object 258 */ 259 public JavaTask mainClass(String mainClass) { 260 this.mainClass = mainClass; 261 return this; 262 } 263 264 /** 265 * Sets the name of the module which contains class to be executed. If empty, 266 * the main class is assumed to be in the unnamed module. 267 * @param module the name of the module 268 * @return this task object 269 */ 270 public JavaTask module(String module) { 271 this.module = module; 272 return this; 273 } 274 275 /** 276 * Sets the name of the module and the main class name to be executed. 277 * @param module the name of the module 278 * @param mainClass the name of the class 279 * @return this task object 280 */ 281 public JavaTask module(String module, String mainClass) { 282 this.module = module; 283 this.mainClass = mainClass; 284 return this; 285 } 286 287 /** 288 * Sets the arguments for the class to be executed. 289 * @param classArgs the arguments 290 * @return this task object 291 */ 292 public JavaTask classArgs(String... classArgs) { 293 this.classArgs = Arrays.asList(classArgs); 294 return this; 295 } 296 297 /** 298 * Sets that the standard VM and java options would not be passed 299 * to the new VM instance. If this method is not called, the default behavior 300 * is that the options will be passed to the new VM instance. 301 * 302 * @return this task object 303 */ 304 public JavaTask ignoreStandardOptions() { 305 standardOptions = EMPTY_LIST; 306 return this; 307 } 308 309 /** 310 * Filters the standard VM and java options. 311 * @param filter a filter 312 * @return this task object 313 */ 314 public JavaTask filterStandardOption(Function<List<String>, List<String>> filter) { 315 standardOptions = filter.apply(standardOptions); 316 return this; 317 } 318 319 /** 320 * Filters the standard VM and java options by taking away a particular option 321 * and a specified number or parameters after it. 322 * 323 * @param option the option, such as {@code --limit-modules} 324 * @param parameterCount number of parameters to remove after the option. 325 * @return this task object 326 */ 327 public JavaTask ignoreStandardOption(String option, int parameterCount) { 328 return filterStandardOption(os -> { 329 List<String> filtered = new ArrayList<>(); 330 for(int i = 0; i < os.size(); i++) { 331 if(os.get(i).equals(option)) 332 i += parameterCount; 333 else 334 filtered.add(os.get(i)); 335 } 336 return filtered; 337 }); 338 } 339 340 /** 341 * Filters the standard VM and java options so that there are no options which 342 * affects a set of resolvable modules or a set of accessible APIs. 343 * 344 * @return this task object 345 */ 346 public JavaTask ignoreStandardModuleOptions() { 347 moduleOptions.entrySet().stream() 348 .forEach(e -> ignoreStandardOption(e.getKey(), e.getValue())); 349 return this; 350 } 351 352 /** 353 * {@inheritDoc} 354 * @return the name "java" 355 */ 356 @Override 357 public String name() { 358 return "java"; 359 } 360 361 /** 362 * Calls the Java launcher with the arguments as currently configured. 363 * @return a Result object indicating the outcome of the task 364 * and the content of any output written to stdout or stderr. 365 * @throws TaskError if the outcome of the task is not as expected. 366 */ 367 @Override 368 public Task.Result run() { 369 List<String> args = new ArrayList<>(standardOptions); 370 if (classPath != null) { 371 args.add(CLASS_PATH); 372 args.add(classPath.stream().collect(joining(","))); 373 } 374 if (modulePath != null) { 375 args.add(MODULE_PATH); 376 args.add(modulePath.stream().collect(joining(","))); 377 } 378 if (upgradeModulePath != null) { 379 args.add(UPGRADE_MODULE_PATH); 380 args.add(upgradeModulePath.stream().collect(joining(","))); 381 } 382 if (addExports != null) 383 addExports.stream().forEach(s -> { 384 args.add(ADD_EXPORTS); 385 args.add(s); 386 }); 387 if (addReads != null) 388 addReads.stream().forEach(s -> { 389 args.add(ADD_READS); 390 args.add(s); 391 }); 392 if (patchModule != null) 393 patchModule.stream().forEach(s -> { 394 args.add(PATCH_MODULE); 395 args.add(s); 396 }); 397 if (addModules != null) { 398 args.add(ADD_MODULES); 399 args.add(addModules.stream().collect(joining(","))); 400 } 401 if (limitModules != null) { 402 args.add(LIMIT_MODULES); 403 args.add(limitModules.stream().collect(joining(","))); 404 } 405 if (vmOptions != null) 406 args.addAll(vmOptions); 407 if (mainClass != null) 408 if (module != null) { 409 args.add("-m"); 410 args.add(module + "/" + mainClass); 411 } else 412 args.add(mainClass); 413 if (classArgs != null) 414 args.addAll(classArgs); 415 return run(Tool.JAVA, args); 416 } 417 418 private void patchModule(String module, Stream<String> files) { 419 this.patchModule.add(module + "=" + 420 files.collect(joining(","))); 421 } 422 423 //this is a list of options to ignore in ignoreStandardModuleOptions() 424 private static final Map<String, Integer> moduleOptions = 425 Map.of(LIMIT_MODULES, 1, 426 UPGRADE_MODULE_PATH, 1, 427 ADD_MODULES, 1, 428 ADD_READS, 1, 429 ADD_EXPORTS, 1, 430 PATCH_MODULE, 1); 431 }