jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java
author redestad
Thu, 21 Apr 2016 13:39:53 +0200
changeset 37593 824750ada3d6
parent 36511 9d0388c6b336
child 37779 7c84df693837
permissions -rw-r--r--
8154231: Simplify access to System properties from JDK code Reviewed-by: rriggs, chegar, weijun

/*
 * Copyright (c) 2015, 2016, 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 java.lang.module;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

import jdk.internal.jimage.ImageLocation;
import jdk.internal.jimage.ImageReader;
import jdk.internal.jimage.ImageReaderFactory;
import jdk.internal.module.SystemModules;
import jdk.internal.module.ModulePatcher;
import jdk.internal.perf.PerfCounter;

/**
 * A {@code ModuleFinder} that finds modules that are linked into the
 * run-time image.
 *
 * The modules linked into the run-time image are assumed to have the
 * ConcealedPackages attribute.
 */

class SystemModuleFinder implements ModuleFinder {

    private static final PerfCounter initTime
        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.initTime");
    private static final PerfCounter moduleCount
        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.modules");
    private static final PerfCounter packageCount
        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.packages");
    private static final PerfCounter exportsCount
        = PerfCounter.newPerfCounter("jdk.module.finder.jimage.exports");
    // ImageReader used to access all modules in the image
    private static final ImageReader imageReader;

    // the set of modules in the run-time image
    private static final Set<ModuleReference> modules;

    // maps module name to module reference
    private static final Map<String, ModuleReference> nameToModule;

    /**
     * For now, the module references are created eagerly on the assumption
     * that service binding will require all modules to be located.
     */
    static {
        long t0 = System.nanoTime();
        imageReader = ImageReaderFactory.getImageReader();

        String[] moduleNames = SystemModules.MODULE_NAMES;
        ModuleDescriptor[] descriptors = null;

        boolean fastLoad = System.getProperty("jdk.installed.modules.disable") == null;
        if (fastLoad) {
            // fast loading of ModuleDescriptor of installed modules
            descriptors = SystemModules.modules();
        }

        int n = moduleNames.length;
        moduleCount.add(n);

        Set<ModuleReference> mods = new HashSet<>(n);
        Map<String, ModuleReference> map = new HashMap<>(n);

        for (int i = 0; i < n; i++) {
            String mn = moduleNames[i];
            ModuleDescriptor md;
            if (fastLoad) {
                md = descriptors[i];
            } else {
                // fallback to read module-info.class
                // if fast loading of ModuleDescriptors is disabled
                ImageLocation location = imageReader.findLocation(mn, "module-info.class");
                md = ModuleDescriptor.read(imageReader.getResourceBuffer(location));
            }
            if (!md.name().equals(mn))
                throw new InternalError();

            // create the ModuleReference

            URI uri = URI.create("jrt:/" + mn);

            Supplier<ModuleReader> readerSupplier = new Supplier<>() {
                @Override
                public ModuleReader get() {
                    return new ImageModuleReader(mn, uri);
                }
            };

            ModuleReference mref = new ModuleReference(md, uri, readerSupplier);

            // may need a reference to a patched module if -Xpatch specified
            mref = ModulePatcher.interposeIfNeeded(mref);

            mods.add(mref);
            map.put(mn, mref);

            // counters
            packageCount.add(md.packages().size());
            exportsCount.add(md.exports().size());
        }

        modules = Collections.unmodifiableSet(mods);
        nameToModule = map;

        initTime.addElapsedTimeFrom(t0);
    }

    SystemModuleFinder() { }

    @Override
    public Optional<ModuleReference> find(String name) {
        Objects.requireNonNull(name);
        return Optional.ofNullable(nameToModule.get(name));
    }

    @Override
    public Set<ModuleReference> findAll() {
        return modules;
    }


    /**
     * A ModuleReader for reading resources from a module linked into the
     * run-time image.
     */
    static class ImageModuleReader implements ModuleReader {
        private final String module;
        private volatile boolean closed;

        /**
         * If there is a security manager set then check permission to
         * connect to the run-time image.
         */
        private static void checkPermissionToConnect(URI uri) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                try {
                    URLConnection uc = uri.toURL().openConnection();
                    sm.checkPermission(uc.getPermission());
                } catch (IOException ioe) {
                    throw new UncheckedIOException(ioe);
                }
            }
        }

        ImageModuleReader(String module, URI uri) {
            checkPermissionToConnect(uri);
            this.module = module;
        }

        /**
         * Returns the ImageLocation for the given resource, {@code null}
         * if not found.
         */
        private ImageLocation findImageLocation(String name) throws IOException {
            if (closed)
                throw new IOException("ModuleReader is closed");

            if (imageReader != null) {
                return imageReader.findLocation(module, name);
            } else {
                // not an images build
                return null;
            }
        }

        @Override
        public Optional<URI> find(String name) throws IOException {
            ImageLocation location = findImageLocation(name);
            if (location != null) {
                URI u = URI.create("jrt:/" + module + "/" + name);
                return Optional.of(u);
            } else {
                return Optional.empty();
            }
        }

        @Override
        public Optional<InputStream> open(String name) throws IOException {
            return read(name).map(this::toInputStream);
        }

        private InputStream toInputStream(ByteBuffer bb) { // ## -> ByteBuffer?
            try {
                int rem = bb.remaining();
                byte[] bytes = new byte[rem];
                bb.get(bytes);
                return new ByteArrayInputStream(bytes);
            } finally {
                release(bb);
            }
        }

        @Override
        public Optional<ByteBuffer> read(String name) throws IOException {
            ImageLocation location = findImageLocation(name);
            if (location != null) {
                return Optional.of(imageReader.getResourceBuffer(location));
            } else {
                return Optional.empty();
            }
        }

        @Override
        public void release(ByteBuffer bb) {
            ImageReader.releaseByteBuffer(bb);
        }

        @Override
        public void close() {
            // nothing else to do
            closed = true;
        }
    }

}