jdk/make/src/classes/build/tools/module/ModuleArchive.java
author chegar
Wed, 03 Dec 2014 14:22:58 +0000
changeset 27565 729f9700483a
child 27934 2b4d774b50dc
permissions -rw-r--r--
8049367: Modular Run-Time Images Reviewed-by: chegar, dfuchs, ihse, joehw, mullan, psandoz, wetmore Contributed-by: alan.bateman@oracle.com, alex.buckley@oracle.com, bradford.wetmore@oracle.com, chris.hegarty@oracle.com, erik.joelsson@oracle.com, james.laskey@oracle.com, jonathan.gibbons@oracle.com, karen.kinnear@oracle.com, magnus.ihse.bursie@oracle.com, mandy.chung@oracle.com, mark.reinhold@oracle.com, paul.sandoz@oracle.com, sundararajan.athijegannathan@oracle.com

/*
 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package build.tools.module;

import jdk.internal.jimage.Archive;
import jdk.internal.jimage.Resource;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;

/**
 * An Archive backed by an exploded representation on disk.
 */
public class ModuleArchive implements Archive {
    private final Path classes;
    private final Path cmds;
    private final Path libs;
    private final Path configs;
    private final String moduleName;

    public ModuleArchive(String moduleName, Path classes, Path cmds,
                         Path libs, Path configs) {
        this.moduleName = moduleName;
        this.classes = classes;
        this.cmds = cmds;
        this.libs = libs;
        this.configs = configs;
    }

    @Override
    public String moduleName() {
        return moduleName;
    }

    @Override
    public void visitResources(Consumer<Resource> consumer) {
        if (classes == null)
            return;
        try{
            Files.walk(classes)
                    .sorted()
                    .filter(p -> !Files.isDirectory(p)
                            && !classes.relativize(p).toString().startsWith("_the.")
                            && !classes.relativize(p).toString().equals("javac_state"))
                    .map(this::toResource)
                    .forEach(consumer::accept);
        } catch (IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
    }

    private Resource toResource(Path path) {
        try {
            return new Resource(classes.relativize(path).toString().replace('\\','/'),
                                Files.size(path),
                                0 /* no compression support yet */);
        } catch (IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
    }

    private enum Section {
        CLASSES,
        CMDS,
        LIBS,
        CONFIGS
    }

    @Override
    public void visitEntries(Consumer<Entry> consumer) {
        try{
            if (classes != null)
                Files.walk(classes)
                        .sorted()
                        .filter(p -> !Files.isDirectory(p)
                                && !classes.relativize(p).toString().startsWith("_the.")
                                && !classes.relativize(p).toString().equals("javac_state"))
                        .map(p -> toEntry(p, classes, Section.CLASSES))
                        .forEach(consumer::accept);
            if (cmds != null)
                Files.walk(cmds)
                        .filter(p -> !Files.isDirectory(p))
                        .map(p -> toEntry(p, cmds, Section.CMDS))
                        .forEach(consumer::accept);
            if (libs != null)
                Files.walk(libs)
                        .filter(p -> !Files.isDirectory(p))
                        .map(p -> toEntry(p, libs, Section.LIBS))
                        .forEach(consumer::accept);
            if (configs != null)
                Files.walk(configs)
                        .filter(p -> !Files.isDirectory(p))
                        .map(p -> toEntry(p, configs, Section.CONFIGS))
                        .forEach(consumer::accept);
        } catch (IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
    }

    private static class FileEntry implements Entry {
        private final String name;
        private final InputStream is;
        private final boolean isDirectory;
        private final Section section;
        FileEntry(String name, InputStream is,
                  boolean isDirectory, Section section) {
            this.name = name;
            this.is = is;
            this.isDirectory = isDirectory;
            this.section = section;
        }
        public String getName() {
            return name;
        }
        public Section getSection() {
            return section;
        }
        public InputStream getInputStream() {
            return is;
        }
        public boolean isDirectory() {
            return isDirectory;
        }
    }

    private Entry toEntry(Path entryPath, Path basePath, Section section) {
        try {
            return new FileEntry(basePath.relativize(entryPath).toString().replace('\\', '/'),
                                 Files.newInputStream(entryPath), false,
                                 section);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public Consumer<Entry> defaultImageWriter(Path path, OutputStream out) {
        return new DefaultEntryWriter(path, out);
    }

    private static class DefaultEntryWriter implements Consumer<Archive.Entry> {
        private final Path root;
        private final OutputStream out;

        DefaultEntryWriter(Path root, OutputStream out) {
            this.root = root;
            this.out = out;
        }

        @Override
        public void accept(Archive.Entry entry) {
            try {
                FileEntry e = (FileEntry)entry;
                Section section = e.getSection();
                String filename = e.getName();

                try (InputStream in = entry.getInputStream()) {
                    switch (section) {
                        case CLASSES:
                            if (!filename.startsWith("_the.") && !filename.equals("javac_state"))
                                writeEntry(in);
                            break;
                        case LIBS:
                            writeEntry(in, destFile(nativeDir(filename), filename));
                            break;
                        case CMDS:
                            Path path = destFile("bin", filename);
                            writeEntry(in, path);
                            path.toFile().setExecutable(true);
                            break;
                        case CONFIGS:
                            writeEntry(in, destFile("conf", filename));
                            break;
                        default:
                            throw new InternalError("unexpected entry: " + filename);
                    }
                }
            } catch (IOException x) {
                throw new UncheckedIOException(x);
            }
        }

        private Path destFile(String dir, String filename) {
            return root.resolve(dir).resolve(filename);
        }

        private static void writeEntry(InputStream in, Path dstFile) throws IOException {
            if (Files.notExists(dstFile.getParent()))
                Files.createDirectories(dstFile.getParent());
            Files.copy(in, dstFile);
        }

        private void writeEntry(InputStream in) throws IOException {
            byte[] buf = new byte[8192];
            int n;
            while ((n = in.read(buf)) > 0)
                out.write(buf, 0, n);
        }

        private static String nativeDir(String filename) {
            if (System.getProperty("os.name").startsWith("Windows")) {
                if (filename.endsWith(".dll"))
                    return "bin";
                 else
                    return "lib";
            } else {
                return "lib";
            }
        }
    }
}