JImageGenerator.java revision 15333:28e938880be3
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 */
23package tests;
24
25import java.io.ByteArrayInputStream;
26import java.io.ByteArrayOutputStream;
27import java.io.File;
28import java.io.FileOutputStream;
29import java.io.IOException;
30import java.io.InputStream;
31import java.io.OutputStream;
32import java.io.PrintStream;
33import java.io.PrintWriter;
34import java.io.StringWriter;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.nio.file.StandardCopyOption;
39import java.util.ArrayList;
40import java.util.Arrays;
41import java.util.Collections;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Objects;
45import java.util.Set;
46import java.util.jar.JarEntry;
47import java.util.jar.JarInputStream;
48import java.util.jar.JarOutputStream;
49import java.util.stream.Collectors;
50import java.util.stream.Stream;
51import java.util.zip.ZipEntry;
52
53import javax.tools.JavaCompiler;
54import javax.tools.StandardJavaFileManager;
55import javax.tools.StandardLocation;
56import javax.tools.ToolProvider;
57
58/**
59 *
60 * A generator for jmods, jars and images.
61 */
62public 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        private Path output;
568        private Path existing;
569
570        public JLinkTask modulePath(String modulePath) {
571            this.modulePath = modulePath;
572            return this;
573        }
574
575        public JLinkTask addJars(Path jars) {
576            this.jars.add(jars);
577            return this;
578        }
579
580        public JLinkTask addJmods(Path jmods) {
581            this.jmods.add(jmods);
582            return this;
583        }
584
585        public JLinkTask pluginModulePath(Path p) {
586            this.pluginModulePath.add(p);
587            return this;
588        }
589
590        public JLinkTask addMods(String moduleName) {
591            this.addMods.add(moduleName);
592            return this;
593        }
594
595        public JLinkTask limitMods(String moduleName) {
596            this.limitMods.add(moduleName);
597            return this;
598        }
599
600        public JLinkTask output(Path output) {
601            this.output = output;
602            return this;
603        }
604
605        public JLinkTask existing(Path existing) {
606            this.existing = existing;
607            return this;
608        }
609
610        public JLinkTask option(String o) {
611            this.options.add(o);
612            return this;
613        }
614
615        private String modulePath() {
616            // This is expect FIRST jmods THEN jars, if you change this, some tests could fail
617            String jmods = toPath(this.jmods);
618            String jars = toPath(this.jars);
619            return jmods + File.pathSeparator + jars;
620        }
621
622        private String toPath(List<Path> paths) {
623            return paths.stream()
624                    .map(Path::toString)
625                    .collect(Collectors.joining(File.pathSeparator));
626        }
627
628        private String[] optionsJLink() {
629            List<String> options = new ArrayList<>();
630            if (output != null) {
631                options.add(OUTPUT_OPTION);
632                options.add(output.toString());
633            }
634            if (!addMods.isEmpty()) {
635                options.add(ADD_MODULES_OPTION);
636                options.add(addMods.stream().collect(Collectors.joining(",")));
637            }
638            if (!limitMods.isEmpty()) {
639                options.add(LIMIT_MODULES_OPTION);
640                options.add(limitMods.stream().collect(Collectors.joining(",")));
641            }
642            if (!jars.isEmpty() || !jmods.isEmpty()) {
643                options.add(MODULE_PATH_OPTION);
644                options.add(modulePath());
645            }
646            if (modulePath != null) {
647                options.add(MODULE_PATH_OPTION);
648                options.add(modulePath);
649            }
650            if (!pluginModulePath.isEmpty()) {
651                options.add(PLUGIN_MODULE_PATH);
652                options.add(toPath(pluginModulePath));
653            }
654            options.addAll(this.options);
655            return options.toArray(new String[options.size()]);
656        }
657
658        private String[] optionsPostProcessJLink() {
659            List<String> options = new ArrayList<>();
660            if (existing != null) {
661                options.add(POST_PROCESS_OPTION);
662                options.add(existing.toString());
663            }
664            options.addAll(this.options);
665            return options.toArray(new String[options.size()]);
666        }
667
668        public Result call() {
669            String[] args = optionsJLink();
670            System.err.println("jlink options: " + optionsPrettyPrint(args));
671            StringWriter writer = new StringWriter();
672            int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer));
673            return new Result(exitCode, writer.toString(), output);
674        }
675
676        public Result callPostProcess() {
677            String[] args = optionsPostProcessJLink();
678            System.err.println("jlink options: " + optionsPrettyPrint(args));
679            StringWriter writer = new StringWriter();
680            int exitCode = jdk.tools.jlink.internal.Main.run(args, new PrintWriter(writer));
681            return new Result(exitCode, writer.toString(), output);
682        }
683    }
684
685    public static class InMemorySourceFile {
686        public final String packageName;
687        public final String className;
688        public final String source;
689
690        public InMemorySourceFile(String packageName, String simpleName, String source) {
691            this.packageName = packageName;
692            this.className = simpleName;
693            this.source = source;
694        }
695    }
696
697    public static class InMemoryFile {
698        private final String path;
699        private final byte[] bytes;
700
701        public String getPath() {
702            return path;
703        }
704
705        public InputStream getBytes() {
706            return new ByteArrayInputStream(bytes);
707        }
708
709        public InMemoryFile(String path, byte[] bytes) {
710            this.path = path;
711            this.bytes = bytes;
712        }
713
714        public InMemoryFile(String path, InputStream is) throws IOException {
715            this(path, readAllBytes(is));
716        }
717    }
718
719    public static byte[] readAllBytes(InputStream is) throws IOException {
720        ByteArrayOutputStream baos = new ByteArrayOutputStream();
721        byte[] buf = new byte[1024];
722        while (true) {
723            int n = is.read(buf);
724            if (n < 0) {
725                break;
726            }
727            baos.write(buf, 0, n);
728        }
729        return baos.toByteArray();
730    }
731}
732