1 /* 2 * Copyright (c) 2015, 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 package tests; 24 25 import java.io.ByteArrayInputStream; 26 import java.io.ByteArrayOutputStream; 27 import java.io.File; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.PrintStream; 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.nio.file.StandardCopyOption; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Objects; 45 import java.util.Set; 46 import java.util.jar.JarEntry; 47 import java.util.jar.JarInputStream; 48 import java.util.jar.JarOutputStream; 49 import java.util.stream.Collectors; 50 import java.util.stream.Stream; 51 import java.util.zip.ZipEntry; 52 53 import javax.tools.JavaCompiler; 54 import javax.tools.StandardJavaFileManager; 55 import javax.tools.StandardLocation; 56 import javax.tools.ToolProvider; 57 58 /** 59 * 60 * A generator for jmods, jars and images. 61 */ 62 public class JImageGenerator { 63 64 public static final String LOAD_ALL_CLASSES_TEMPLATE = "package PACKAGE;\n" 65 + "\n" 66 + "import java.net.URI;\n" 67 + "import java.nio.file.FileSystems;\n" 68 + "import java.nio.file.Files;\n" 69 + "import java.nio.file.Path;\n" 70 + "import java.util.function.Function;\n" 71 + "\n" 72 + "public class CLASS {\n" 73 + " private static long total_time;\n" 74 + " private static long num_classes;\n" 75 + " public static void main(String[] args) throws Exception {\n" 76 + " Function<Path, String> formatter = (path) -> {\n" 77 + " String clazz = path.toString().substring(\"modules/\".length()+1, path.toString().lastIndexOf(\".\"));\n" 78 + " clazz = clazz.substring(clazz.indexOf(\"/\") + 1);\n" 79 + " return clazz.replaceAll(\"/\", \"\\\\.\");\n" 80 + " };\n" 81 + " Files.walk(FileSystems.getFileSystem(URI.create(\"jrt:/\")).getPath(\"/modules/\")).\n" 82 + " filter((p) -> {\n" 83 + " return Files.isRegularFile(p) && p.toString().endsWith(\".class\")\n" 84 + " && !p.toString().endsWith(\"module-info.class\");\n" 85 + " }).\n" 86 + " map(formatter).forEach((clazz) -> {\n" 87 + " try {\n" 88 + " long t = System.currentTimeMillis();\n" 89 + " Class.forName(clazz, false, Thread.currentThread().getContextClassLoader());\n" 90 + " total_time+= System.currentTimeMillis()-t;\n" 91 + " num_classes+=1;\n" 92 + " } catch (IllegalAccessError ex) {\n" 93 + " // Security exceptions can occur, this is not what we are testing\n" 94 + " System.err.println(\"Access error, OK \" + clazz);\n" 95 + " } catch (Exception ex) {\n" 96 + " System.err.println(\"ERROR \" + clazz);\n" 97 + " throw new RuntimeException(ex);\n" 98 + " }\n" 99 + " });\n" 100 + " double res = (double) total_time / num_classes;\n" 101 + " // System.out.println(\"Total time \" + total_time + \" num classes \" + num_classes + \" average \" + res);\n" 102 + " }\n" 103 + "}\n"; 104 105 private static final String OUTPUT_OPTION = "--output"; 106 private static final String POST_PROCESS_OPTION = "--post-process-path"; 107 private static final String MAIN_CLASS_OPTION = "--main-class"; 108 private static final String CLASS_PATH_OPTION = "--class-path"; 109 private static final String MODULE_PATH_OPTION = "--module-path"; 110 private static final String ADD_MODULES_OPTION = "--add-modules"; 111 private static final String LIMIT_MODULES_OPTION = "--limit-modules"; 112 private static final String PLUGIN_MODULE_PATH = "--plugin-module-path"; 113 114 private static final String CMDS_OPTION = "--cmds"; 115 private static final String CONFIG_OPTION = "--config"; 116 private static final String HASH_MODULES_OPTION = "--hash-modules"; 117 private static final String LIBS_OPTION = "--libs"; 118 private static final String MODULE_VERSION_OPTION = "--module-version"; 119 120 private JImageGenerator() {} 121 122 private static String optionsPrettyPrint(String... args) { 123 return Stream.of(args).collect(Collectors.joining(" ")); 124 } 125 126 public static File getJModsDir(File jdkHome) { 127 File jdkjmods = new File(jdkHome, "jmods"); 128 if (!jdkjmods.exists()) { 129 return null; 130 } 131 return jdkjmods; 132 } 133 134 public static Path addFiles(Path module, InMemoryFile... resources) throws IOException { 135 Path tempFile = Files.createTempFile("jlink-test", ""); 136 try (JarInputStream in = new JarInputStream(Files.newInputStream(module)); 137 JarOutputStream out = new JarOutputStream(new FileOutputStream(tempFile.toFile()))) { 138 ZipEntry entry; 139 while ((entry = in.getNextEntry()) != null) { 140 String name = entry.getName(); 141 out.putNextEntry(new ZipEntry(name)); 142 copy(in, out); 143 out.closeEntry(); 144 } 145 for (InMemoryFile r : resources) { 146 addFile(r, out); 147 } 148 } 149 Files.move(tempFile, module, StandardCopyOption.REPLACE_EXISTING); 150 return module; 151 } 152 153 private static void copy(InputStream in, OutputStream out) throws IOException { 154 int len; 155 byte[] buf = new byte[4096]; 156 while ((len = in.read(buf)) > 0) { 157 out.write(buf, 0, len); 158 } 159 } 160 161 public static JModTask getJModTask() { 162 return new JModTask(); 163 } 164 165 public static JLinkTask getJLinkTask() { 166 return new JLinkTask(); 167 } 168 169 public static JImageTask getJImageTask() { 170 return new JImageTask(); 171 } 172 173 private static void addFile(InMemoryFile resource, JarOutputStream target) throws IOException { 174 String fileName = resource.getPath(); 175 fileName = fileName.replace("\\", "/"); 176 String[] ss = fileName.split("/"); 177 Path p = Paths.get(""); 178 for (int i = 0; i < ss.length; ++i) { 179 if (i < ss.length - 1) { 180 if (!ss[i].isEmpty()) { 181 p = p.resolve(ss[i]); 182 JarEntry entry = new JarEntry(p.toString() + "/"); 183 target.putNextEntry(entry); 184 target.closeEntry(); 185 } 186 } else { 187 p = p.resolve(ss[i]); 188 JarEntry entry = new JarEntry(p.toString()); 189 target.putNextEntry(entry); 190 copy(resource.getBytes(), target); 191 target.closeEntry(); 192 } 193 } 194 } 195 196 public static Path createNewFile(Path root, String pathName, String extension) { 197 Path out = root.resolve(pathName + extension); 198 int i = 1; 199 while (Files.exists(out)) { 200 out = root.resolve(pathName + "-" + (++i) + extension); 201 } 202 return out; 203 } 204 205 public static Path generateSources(Path output, String moduleName, List<InMemorySourceFile> sources) throws IOException { 206 Path moduleDir = output.resolve(moduleName); 207 Files.createDirectory(moduleDir); 208 for (InMemorySourceFile source : sources) { 209 Path fileDir = moduleDir; 210 if (!source.packageName.isEmpty()) { 211 String dir = source.packageName.replace('.', File.separatorChar); 212 fileDir = moduleDir.resolve(dir); 213 Files.createDirectories(fileDir); 214 } 215 Files.write(fileDir.resolve(source.className + ".java"), source.source.getBytes()); 216 } 217 return moduleDir; 218 } 219 220 public static Path generateSourcesFromTemplate(Path output, String moduleName, String... classNames) throws IOException { 221 List<InMemorySourceFile> sources = new ArrayList<>(); 222 for (String className : classNames) { 223 String packageName = getPackageName(className); 224 String simpleName = getSimpleName(className); 225 String content = LOAD_ALL_CLASSES_TEMPLATE 226 .replace("CLASS", simpleName); 227 if (packageName.isEmpty()) { 228 content = content.replace("package PACKAGE;", packageName); 229 } else { 230 content = content.replace("PACKAGE", packageName); 231 } 232 sources.add(new InMemorySourceFile(packageName, simpleName, content)); 233 } 234 return generateSources(output, moduleName, sources); 235 } 236 237 public static void generateModuleInfo(Path moduleDir, List<String> packages, String... dependencies) throws IOException { 238 StringBuilder moduleInfoBuilder = new StringBuilder(); 239 Path file = moduleDir.resolve("module-info.java"); 240 String moduleName = moduleDir.getFileName().toString(); 241 moduleInfoBuilder.append("module ").append(moduleName).append("{\n"); 242 for (String dep : dependencies) { 243 moduleInfoBuilder.append("requires ").append(dep).append(";\n"); 244 } 245 for (String pkg : packages) { 246 if (!pkg.trim().isEmpty()) { 247 moduleInfoBuilder.append("exports ").append(pkg).append(";\n"); 248 } 249 } 250 moduleInfoBuilder.append("}"); 251 Files.write(file, moduleInfoBuilder.toString().getBytes()); 252 } 253 254 public static void compileSuccess(Path source, Path destination, String... options) throws IOException { 255 if (!compile(source, destination, options)) { 256 throw new AssertionError("Compilation failed."); 257 } 258 } 259 260 public static boolean compile(Path source, Path destination, String... options) throws IOException { 261 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 262 try (StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null)) { 263 List<Path> sources 264 = Files.find(source, Integer.MAX_VALUE, 265 (file, attrs) -> file.toString().endsWith(".java")) 266 .collect(Collectors.toList()); 267 268 Files.createDirectories(destination); 269 jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Collections.singleton(destination)); 270 271 List<String> opts = Arrays.asList(options); 272 JavaCompiler.CompilationTask task 273 = compiler.getTask(null, jfm, null, opts, null, 274 jfm.getJavaFileObjectsFromPaths(sources)); 275 List<String> list = new ArrayList<>(opts); 276 list.addAll(sources.stream() 277 .map(Path::toString) 278 .collect(Collectors.toList())); 279 System.err.println("javac options: " + optionsPrettyPrint(list.toArray(new String[list.size()]))); 280 return task.call(); 281 } 282 } 283 284 public static Path createJarFile(Path jarfile, Path dir) throws IOException { 285 return createJarFile(jarfile, dir, Paths.get(".")); 286 } 287 288 public static Path createJarFile(Path jarfile, Path dir, Path file) throws IOException { 289 // create the target directory 290 Path parent = jarfile.getParent(); 291 if (parent != null) 292 Files.createDirectories(parent); 293 294 List<Path> entries = Files.find(dir.resolve(file), Integer.MAX_VALUE, 295 (p, attrs) -> attrs.isRegularFile()) 296 .map(dir::relativize) 297 .collect(Collectors.toList()); 298 299 try (OutputStream out = Files.newOutputStream(jarfile); 300 JarOutputStream jos = new JarOutputStream(out)) { 301 for (Path entry : entries) { 302 // map the file path to a name in the JAR file 303 Path normalized = entry.normalize(); 304 String name = normalized 305 .subpath(0, normalized.getNameCount()) // drop root 306 .toString() 307 .replace(File.separatorChar, '/'); 308 309 jos.putNextEntry(new JarEntry(name)); 310 Files.copy(dir.resolve(entry), jos); 311 } 312 } 313 return jarfile; 314 } 315 316 public static Set<String> getModuleContent(Path module) { 317 Result result = JImageGenerator.getJModTask() 318 .jmod(module) 319 .list(); 320 result.assertSuccess(); 321 return Stream.of(result.getMessage().split("\r?\n")) 322 .collect(Collectors.toSet()); 323 } 324 325 public static void checkModule(Path module, Set<String> expected) throws IOException { 326 Set<String> actual = getModuleContent(module); 327 if (!Objects.equals(actual, expected)) { 328 Set<String> unexpected = new HashSet<>(actual); 329 unexpected.removeAll(expected); 330 Set<String> notFound = new HashSet<>(expected); 331 notFound.removeAll(actual); 332 System.err.println("Unexpected files:"); 333 unexpected.forEach(s -> System.err.println("\t" + s)); 334 System.err.println("Not found files:"); 335 notFound.forEach(s -> System.err.println("\t" + s)); 336 throw new AssertionError("Module check failed."); 337 } 338 } 339 340 public static class JModTask { 341 static final java.util.spi.ToolProvider JMOD_TOOL = 342 java.util.spi.ToolProvider.findFirst("jmod").get(); 343 344 private final List<Path> classpath = new ArrayList<>(); 345 private final List<Path> libs = new ArrayList<>(); 346 private final List<Path> cmds = new ArrayList<>(); 347 private final List<Path> config = new ArrayList<>(); 348 private final List<Path> jars = new ArrayList<>(); 349 private final List<Path> jmods = new ArrayList<>(); 350 private final List<String> options = new ArrayList<>(); 351 private Path output; 352 private String hashModules; 353 private String mainClass; 354 private String moduleVersion; 355 356 public JModTask addNativeLibraries(Path cp) { 357 this.libs.add(cp); 358 return this; 359 } 360 361 public JModTask hashModules(String hash) { 362 this.hashModules = hash; 363 return this; 364 } 365 366 public JModTask addCmds(Path cp) { 367 this.cmds.add(cp); 368 return this; 369 } 370 371 public JModTask addClassPath(Path cp) { 372 this.classpath.add(cp); 373 return this; 374 } 375 376 public JModTask addConfig(Path cp) { 377 this.config.add(cp); 378 return this; 379 } 380 381 public JModTask addJars(Path jars) { 382 this.jars.add(jars); 383 return this; 384 } 385 386 public JModTask addJmods(Path jmods) { 387 this.jmods.add(jmods); 388 return this; 389 } 390 391 public JModTask jmod(Path output) { 392 this.output = output; 393 return this; 394 } 395 396 public JModTask moduleVersion(String moduleVersion) { 397 this.moduleVersion = moduleVersion; 398 return this; 399 } 400 401 public JModTask mainClass(String mainClass) { 402 this.mainClass = mainClass; 403 return this; 404 } 405 406 public JModTask option(String o) { 407 this.options.add(o); 408 return this; 409 } 410 411 private String modulePath() { 412 // This is expect FIRST jmods THEN jars, if you change this, some tests could fail 413 String jmods = toPath(this.jmods); 414 String jars = toPath(this.jars); 415 return jmods + File.pathSeparator + jars; 416 } 417 418 private String toPath(List<Path> paths) { 419 return paths.stream() 420 .map(Path::toString) 421 .collect(Collectors.joining(File.pathSeparator)); 422 } 423 424 private String[] optionsJMod(String cmd) { 425 List<String> options = new ArrayList<>(); 426 options.add(cmd); 427 if (!cmds.isEmpty()) { 428 options.add(CMDS_OPTION); 429 options.add(toPath(cmds)); 430 } 431 if (!config.isEmpty()) { 432 options.add(CONFIG_OPTION); 433 options.add(toPath(config)); 434 } 435 if (hashModules != null) { 436 options.add(HASH_MODULES_OPTION); 437 options.add(hashModules); 438 } 439 if (mainClass != null) { 440 options.add(MAIN_CLASS_OPTION); 441 options.add(mainClass); 442 } 443 if (!libs.isEmpty()) { 444 options.add(LIBS_OPTION); 445 options.add(toPath(libs)); 446 } 447 if (!classpath.isEmpty()) { 448 options.add(CLASS_PATH_OPTION); 449 options.add(toPath(classpath)); 450 } 451 if (!jars.isEmpty() || !jmods.isEmpty()) { 452 options.add(MODULE_PATH_OPTION); 453 options.add(modulePath()); 454 } 455 if (moduleVersion != null) { 456 options.add(MODULE_VERSION_OPTION); 457 options.add(moduleVersion); 458 } 459 options.addAll(this.options); 460 if (output != null) { 461 options.add(output.toString()); 462 } 463 return options.toArray(new String[options.size()]); 464 } 465 466 public Result create() { 467 return cmd("create"); 468 } 469 470 public Result list() { 471 return cmd("list"); 472 } 473 474 public Result call() { 475 return cmd(""); 476 } 477 478 private Result cmd(String cmd) { 479 String[] args = optionsJMod(cmd); 480 System.err.println("jmod options: " + optionsPrettyPrint(args)); 481 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 482 PrintStream ps = new PrintStream(baos); 483 int exitCode = JMOD_TOOL.run(ps, ps, args); 484 String msg = new String(baos.toByteArray()); 485 return new Result(exitCode, msg, output); 486 } 487 } 488 489 public static String getPackageName(String canonicalName) { 490 int index = canonicalName.lastIndexOf('.'); 491 return index > 0 ? canonicalName.substring(0, index) : ""; 492 } 493 494 public static String getSimpleName(String canonicalName) { 495 int index = canonicalName.lastIndexOf('.'); 496 return canonicalName.substring(index + 1); 497 } 498 499 public static class JImageTask { 500 501 private final List<Path> pluginModulePath = new ArrayList<>(); 502 private final List<String> options = new ArrayList<>(); 503 private Path dir; 504 private Path image; 505 506 public JImageTask pluginModulePath(Path p) { 507 this.pluginModulePath.add(p); 508 return this; 509 } 510 511 public JImageTask image(Path image) { 512 this.image = image; 513 return this; 514 } 515 516 public JImageTask dir(Path dir) { 517 this.dir = dir; 518 return this; 519 } 520 521 public JImageTask option(String o) { 522 this.options.add(o); 523 return this; 524 } 525 526 private String toPath(List<Path> paths) { 527 return paths.stream() 528 .map(Path::toString) 529 .collect(Collectors.joining(File.pathSeparator)); 530 } 531 532 private String[] optionsJImage(String cmd) { 533 List<String> options = new ArrayList<>(); 534 options.add(cmd); 535 if (dir != null) { 536 options.add("--dir"); 537 options.add(dir.toString()); 538 } 539 if (!pluginModulePath.isEmpty()) { 540 options.add(PLUGIN_MODULE_PATH); 541 options.add(toPath(pluginModulePath)); 542 } 543 options.addAll(this.options); 544 options.add(image.toString()); 545 return options.toArray(new String[options.size()]); 546 } 547 548 private Result cmd(String cmd, Path returnPath) { 549 String[] args = optionsJImage(cmd); 550 System.err.println("jimage options: " + optionsPrettyPrint(args)); 551 StringWriter writer = new StringWriter(); 552 int exitCode = jdk.tools.jimage.Main.run(args, new PrintWriter(writer)); 553 return new Result(exitCode, writer.toString(), returnPath); 554 } 555 556 public Result extract() { 557 return cmd("extract", dir); 558 } 559 } 560 561 public static class JLinkTask { 562 static final java.util.spi.ToolProvider JLINK_TOOL = 563 java.util.spi.ToolProvider.findFirst("jlink").get(); 564 565 private final List<Path> jars = new ArrayList<>(); 566 private final List<Path> jmods = new ArrayList<>(); 567 private final List<Path> pluginModulePath = new ArrayList<>(); 568 private final List<String> addMods = new ArrayList<>(); 569 private final List<String> limitMods = new ArrayList<>(); 570 private final List<String> options = new ArrayList<>(); 571 private String modulePath; 572 // if you want to specifiy repeated --module-path option 573 private String repeatedModulePath; 574 // if you want to specifiy repeated --limit-modules option 575 private String repeatedLimitMods; 576 private Path output; 577 private Path existing; 578 579 public JLinkTask modulePath(String modulePath) { 580 this.modulePath = modulePath; 581 return this; 582 } 583 584 public JLinkTask repeatedModulePath(String modulePath) { 585 this.repeatedModulePath = modulePath; 586 return this; 587 } 588 589 public JLinkTask addJars(Path jars) { 590 this.jars.add(jars); 591 return this; 592 } 593 594 public JLinkTask addJmods(Path jmods) { 595 this.jmods.add(jmods); 596 return this; 597 } 598 599 public JLinkTask pluginModulePath(Path p) { 600 this.pluginModulePath.add(p); 601 return this; 602 } 603 604 public JLinkTask addMods(String moduleName) { 605 this.addMods.add(moduleName); 606 return this; 607 } 608 609 public JLinkTask limitMods(String moduleName) { 610 this.limitMods.add(moduleName); 611 return this; 612 } 613 614 public JLinkTask repeatedLimitMods(String modules) { 615 this.repeatedLimitMods = modules; 616 return this; 617 } 618 619 public JLinkTask output(Path output) { 620 this.output = output; 621 return this; 622 } 623 624 public JLinkTask existing(Path existing) { 625 this.existing = existing; 626 return this; 627 } 628 629 public JLinkTask option(String o) { 630 this.options.add(o); 631 return this; 632 } 633 634 private String modulePath() { 635 // This is expect FIRST jmods THEN jars, if you change this, some tests could fail 636 String jmods = toPath(this.jmods); 637 String jars = toPath(this.jars); 638 return jmods + File.pathSeparator + jars; 639 } 640 641 private String toPath(List<Path> paths) { 642 return paths.stream() 643 .map(Path::toString) 644 .collect(Collectors.joining(File.pathSeparator)); 645 } 646 647 private String[] optionsJLink() { 648 List<String> options = new ArrayList<>(); 649 if (output != null) { 650 options.add(OUTPUT_OPTION); 651 options.add(output.toString()); 652 } 653 if (!addMods.isEmpty()) { 654 options.add(ADD_MODULES_OPTION); 655 options.add(addMods.stream().collect(Collectors.joining(","))); 656 } 657 if (!limitMods.isEmpty()) { 658 options.add(LIMIT_MODULES_OPTION); 659 options.add(limitMods.stream().collect(Collectors.joining(","))); 660 } 661 if (repeatedLimitMods != null) { 662 options.add(LIMIT_MODULES_OPTION); 663 options.add(repeatedLimitMods); 664 } 665 if (!jars.isEmpty() || !jmods.isEmpty()) { 666 options.add(MODULE_PATH_OPTION); 667 options.add(modulePath()); 668 } 669 if (modulePath != null) { 670 options.add(MODULE_PATH_OPTION); 671 options.add(modulePath); 672 } 673 if (repeatedModulePath != null) { 674 options.add(MODULE_PATH_OPTION); 675 options.add(repeatedModulePath); 676 } 677 if (!pluginModulePath.isEmpty()) { 678 options.add(PLUGIN_MODULE_PATH); 679 options.add(toPath(pluginModulePath)); 680 } 681 options.addAll(this.options); 682 return options.toArray(new String[options.size()]); 683 } 684 685 private String[] optionsPostProcessJLink() { 686 List<String> options = new ArrayList<>(); 687 if (existing != null) { 688 options.add(POST_PROCESS_OPTION); 689 options.add(existing.toString()); 690 } 691 options.addAll(this.options); 692 return options.toArray(new String[options.size()]); 693 } 694 695 public Result call() { 696 String[] args = optionsJLink(); 697 System.err.println("jlink options: " + optionsPrettyPrint(args)); 698 StringWriter writer = new StringWriter(); 699 PrintWriter pw = new PrintWriter(writer); 700 int exitCode = JLINK_TOOL.run(pw, pw, args); 701 return new Result(exitCode, writer.toString(), output); 702 } 703 704 public Result callPostProcess() { 705 String[] args = optionsPostProcessJLink(); 706 System.err.println("jlink options: " + optionsPrettyPrint(args)); 707 StringWriter writer = new StringWriter(); 708 PrintWriter pw = new PrintWriter(writer); 709 int exitCode = JLINK_TOOL.run(pw, pw, args); 710 return new Result(exitCode, writer.toString(), output); 711 } 712 } 713 714 public static class InMemorySourceFile { 715 public final String packageName; 716 public final String className; 717 public final String source; 718 719 public InMemorySourceFile(String packageName, String simpleName, String source) { 720 this.packageName = packageName; 721 this.className = simpleName; 722 this.source = source; 723 } 724 } 725 726 public static class InMemoryFile { 727 private final String path; 728 private final byte[] bytes; 729 730 public String getPath() { 731 return path; 732 } 733 734 public InputStream getBytes() { 735 return new ByteArrayInputStream(bytes); 736 } 737 738 public InMemoryFile(String path, byte[] bytes) { 739 this.path = path; 740 this.bytes = bytes; 741 } 742 743 public InMemoryFile(String path, InputStream is) throws IOException { 744 this(path, readAllBytes(is)); 745 } 746 } 747 748 public static byte[] readAllBytes(InputStream is) throws IOException { 749 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 750 byte[] buf = new byte[1024]; 751 while (true) { 752 int n = is.read(buf); 753 if (n < 0) { 754 break; 755 } 756 baos.write(buf, 0, n); 757 } 758 return baos.toByteArray(); 759 } 760 }