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 342 private final List<Path> classpath = new ArrayList<>(); 343 private final List<Path> libs = new ArrayList<>(); 344 private final List<Path> cmds = new ArrayList<>(); 345 private final List<Path> config = new ArrayList<>(); 346 private final List<Path> jars = new ArrayList<>(); 347 private final List<Path> jmods = new ArrayList<>(); 348 private final List<String> options = new ArrayList<>(); 349 private Path output; 350 private String hashModules; 351 private String mainClass; 352 private String moduleVersion; 353 354 public JModTask addNativeLibraries(Path cp) { 355 this.libs.add(cp); 356 return this; 357 } 358 359 public JModTask hashModules(String hash) { 360 this.hashModules = hash; 361 return this; 362 } 363 364 public JModTask addCmds(Path cp) { 365 this.cmds.add(cp); 366 return this; 367 } 368 369 public JModTask addClassPath(Path cp) { 370 this.classpath.add(cp); 371 return this; 372 } 373 374 public JModTask addConfig(Path cp) { 375 this.config.add(cp); 376 return this; 377 } 378 379 public JModTask addJars(Path jars) { 380 this.jars.add(jars); 381 return this; 382 } 383 384 public JModTask addJmods(Path jmods) { 385 this.jmods.add(jmods); 386 return this; 387 } 388 389 public JModTask jmod(Path output) { 390 this.output = output; 391 return this; 392 } 393 394 public JModTask moduleVersion(String moduleVersion) { 395 this.moduleVersion = moduleVersion; 396 return this; 397 } 398 399 public JModTask mainClass(String mainClass) { 400 this.mainClass = mainClass; 401 return this; 402 } 403 404 public JModTask option(String o) { 405 this.options.add(o); 406 return this; 407 } 408 409 private String modulePath() { 410 // This is expect FIRST jmods THEN jars, if you change this, some tests could fail 411 String jmods = toPath(this.jmods); 412 String jars = toPath(this.jars); 413 return jmods + File.pathSeparator + jars; 414 } 415 416 private String toPath(List<Path> paths) { 417 return paths.stream() 418 .map(Path::toString) 419 .collect(Collectors.joining(File.pathSeparator)); 420 } 421 422 private String[] optionsJMod(String cmd) { 423 List<String> options = new ArrayList<>(); 424 options.add(cmd); 425 if (!cmds.isEmpty()) { 426 options.add(CMDS_OPTION); 427 options.add(toPath(cmds)); 428 } 429 if (!config.isEmpty()) { 430 options.add(CONFIG_OPTION); 431 options.add(toPath(config)); 432 } 433 if (hashModules != null) { 434 options.add(HASH_MODULES_OPTION); 435 options.add(hashModules); 436 } 437 if (mainClass != null) { 438 options.add(MAIN_CLASS_OPTION); 439 options.add(mainClass); 440 } 441 if (!libs.isEmpty()) { 442 options.add(LIBS_OPTION); 443 options.add(toPath(libs)); 444 } 445 if (!classpath.isEmpty()) { 446 options.add(CLASS_PATH_OPTION); 447 options.add(toPath(classpath)); 448 } 449 if (!jars.isEmpty() || !jmods.isEmpty()) { 450 options.add(MODULE_PATH_OPTION); 451 options.add(modulePath()); 452 } 453 if (moduleVersion != null) { 454 options.add(MODULE_VERSION_OPTION); 455 options.add(moduleVersion); 456 } 457 options.addAll(this.options); 458 if (output != null) { 459 options.add(output.toString()); 460 } 461 return options.toArray(new String[options.size()]); 462 } 463 464 public Result create() { 465 return cmd("create"); 466 } 467 468 public Result list() { 469 return cmd("list"); 470 } 471 472 public Result call() { 473 return cmd(""); 474 } 475 476 private Result cmd(String cmd) { 477 String[] args = optionsJMod(cmd); 478 System.err.println("jmod options: " + optionsPrettyPrint(args)); 479 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 480 int exitCode = jdk.tools.jmod.Main.run(args, new PrintStream(baos)); 481 String msg = new String(baos.toByteArray()); 482 return new Result(exitCode, msg, output); 483 } 484 } 485 486 public static String getPackageName(String canonicalName) { 487 int index = canonicalName.lastIndexOf('.'); 488 return index > 0 ? canonicalName.substring(0, index) : ""; 489 } 490 491 public static String getSimpleName(String canonicalName) { 492 int index = canonicalName.lastIndexOf('.'); 493 return canonicalName.substring(index + 1); 494 } 495 496 public static class JImageTask { 497 498 private final List<Path> pluginModulePath = new ArrayList<>(); 499 private final List<String> options = new ArrayList<>(); 500 private Path dir; 501 private Path image; 502 503 public JImageTask pluginModulePath(Path p) { 504 this.pluginModulePath.add(p); 505 return this; 506 } 507 508 public JImageTask image(Path image) { 509 this.image = image; 510 return this; 511 } 512 513 public JImageTask dir(Path dir) { 514 this.dir = dir; 515 return this; 516 } 517 518 public JImageTask option(String o) { 519 this.options.add(o); 520 return this; 521 } 522 523 private String toPath(List<Path> paths) { 524 return paths.stream() 525 .map(Path::toString) 526 .collect(Collectors.joining(File.pathSeparator)); 527 } 528 529 private String[] optionsJImage(String cmd) { 530 List<String> options = new ArrayList<>(); 531 options.add(cmd); 532 if (dir != null) { 533 options.add("--dir"); 534 options.add(dir.toString()); 535 } 536 if (!pluginModulePath.isEmpty()) { 537 options.add(PLUGIN_MODULE_PATH); 538 options.add(toPath(pluginModulePath)); 539 } 540 options.addAll(this.options); 541 options.add(image.toString()); 542 return options.toArray(new String[options.size()]); 543 } 544 545 private Result cmd(String cmd, Path returnPath) { 546 String[] args = optionsJImage(cmd); 547 System.err.println("jimage options: " + optionsPrettyPrint(args)); 548 StringWriter writer = new StringWriter(); 549 int exitCode = jdk.tools.jimage.Main.run(args, new PrintWriter(writer)); 550 return new Result(exitCode, writer.toString(), returnPath); 551 } 552 553 public Result extract() { 554 return cmd("extract", dir); 555 } 556 } 557 558 public static class JLinkTask { 559 560 private final List<Path> jars = new ArrayList<>(); 561 private final List<Path> jmods = new ArrayList<>(); 562 private final List<Path> pluginModulePath = new ArrayList<>(); 563 private final List<String> addMods = new ArrayList<>(); 564 private final List<String> limitMods = new ArrayList<>(); 565 private final List<String> options = new ArrayList<>(); 566 private String modulePath; 567 // if you want to specifiy repeated --module-path option 568 private String repeatedModulePath; 569 // if you want to specifiy repeated --limit-modules option 570 private String repeatedLimitMods; 571 private Path output; 572 private Path existing; 573 574 public JLinkTask modulePath(String modulePath) { 575 this.modulePath = modulePath; 576 return this; 577 } 578 579 public JLinkTask repeatedModulePath(String modulePath) { 580 this.repeatedModulePath = modulePath; 581 return this; 582 } 583 584 public JLinkTask addJars(Path jars) { 585 this.jars.add(jars); 586 return this; 587 } 588 589 public JLinkTask addJmods(Path jmods) { 590 this.jmods.add(jmods); 591 return this; 592 } 593 594 public JLinkTask pluginModulePath(Path p) { 595 this.pluginModulePath.add(p); 596 return this; 597 } 598 599 public JLinkTask addMods(String moduleName) { 600 this.addMods.add(moduleName); 601 return this; 602 } 603 604 public JLinkTask limitMods(String moduleName) { 605 this.limitMods.add(moduleName); 606 return this; 607 } 608 609 public JLinkTask repeatedLimitMods(String modules) { 610 this.repeatedLimitMods = modules; 611 return this; 612 } 613 614 public JLinkTask output(Path output) { 615 this.output = output; 616 return this; 617 } 618 619 public JLinkTask existing(Path existing) { 620 this.existing = existing; 621 return this; 622 } 623 624 public JLinkTask option(String o) { 625 this.options.add(o); 626 return this; 627 } 628 629 private String modulePath() { 630 // This is expect FIRST jmods THEN jars, if you change this, some tests could fail 631 String jmods = toPath(this.jmods); 632 String jars = toPath(this.jars); 633 return jmods + File.pathSeparator + jars; 634 } 635 636 private String toPath(List<Path> paths) { 637 return paths.stream() 638 .map(Path::toString) 639 .collect(Collectors.joining(File.pathSeparator)); 640 } 641 642 private String[] optionsJLink() { 643 List<String> options = new ArrayList<>(); 644 if (output != null) { 645 options.add(OUTPUT_OPTION); 646 options.add(output.toString()); 647 } 648 if (!addMods.isEmpty()) { 649 options.add(ADD_MODULES_OPTION); 650 options.add(addMods.stream().collect(Collectors.joining(","))); 651 } 652 if (!limitMods.isEmpty()) { 653 options.add(LIMIT_MODULES_OPTION); 654 options.add(limitMods.stream().collect(Collectors.joining(","))); 655 } 656 if (repeatedLimitMods != null) { 657 options.add(LIMIT_MODULES_OPTION); 658 options.add(repeatedLimitMods); 659 } 660 if (!jars.isEmpty() || !jmods.isEmpty()) { 661 options.add(MODULE_PATH_OPTION); 662 options.add(modulePath()); 663 } 664 if (modulePath != null) { 665 options.add(MODULE_PATH_OPTION); 666 options.add(modulePath); 667 } 668 if (repeatedModulePath != null) { 669 options.add(MODULE_PATH_OPTION); 670 options.add(repeatedModulePath); 671 } 672 if (!pluginModulePath.isEmpty()) { 673 options.add(PLUGIN_MODULE_PATH); 674 options.add(toPath(pluginModulePath)); 675 } 676 options.addAll(this.options); 677 return options.toArray(new String[options.size()]); 678 } 679 680 private String[] optionsPostProcessJLink() { 681 List<String> options = new ArrayList<>(); 682 if (existing != null) { 683 options.add(POST_PROCESS_OPTION); 684 options.add(existing.toString()); 685 } 686 options.addAll(this.options); 687 return options.toArray(new String[options.size()]); 688 } 689 690 public Result call() { 691 String[] args = optionsJLink(); 692 System.err.println("jlink options: " + optionsPrettyPrint(args)); 693 StringWriter writer = new StringWriter(); 694 int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer)); 695 return new Result(exitCode, writer.toString(), output); 696 } 697 698 public Result callPostProcess() { 699 String[] args = optionsPostProcessJLink(); 700 System.err.println("jlink options: " + optionsPrettyPrint(args)); 701 StringWriter writer = new StringWriter(); 702 int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer)); 703 return new Result(exitCode, writer.toString(), output); 704 } 705 } 706 707 public static class InMemorySourceFile { 708 public final String packageName; 709 public final String className; 710 public final String source; 711 712 public InMemorySourceFile(String packageName, String simpleName, String source) { 713 this.packageName = packageName; 714 this.className = simpleName; 715 this.source = source; 716 } 717 } 718 719 public static class InMemoryFile { 720 private final String path; 721 private final byte[] bytes; 722 723 public String getPath() { 724 return path; 725 } 726 727 public InputStream getBytes() { 728 return new ByteArrayInputStream(bytes); 729 } 730 731 public InMemoryFile(String path, byte[] bytes) { 732 this.path = path; 733 this.bytes = bytes; 734 } 735 736 public InMemoryFile(String path, InputStream is) throws IOException { 737 this(path, readAllBytes(is)); 738 } 739 } 740 741 public static byte[] readAllBytes(InputStream is) throws IOException { 742 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 743 byte[] buf = new byte[1024]; 744 while (true) { 745 int n = is.read(buf); 746 if (n < 0) { 747 break; 748 } 749 baos.write(buf, 0, n); 750 } 751 return baos.toByteArray(); 752 } 753 }