JDK

java.lang.Systemクラスのソースコード

java.lang.SystemクラスはJavaプログラマなら真っ先に使うクラスと言える。”Hello World”アプリケーションを書いたときに,「System.out.println(“Hello World!”);」と書いたはずだ。このコードの先頭にあるSystemが,java.lang.Systemクラスである。outはjava.lang.Systemクラスの中でpublicフィールドとして, public  static  final  PrintStream  out  =  null; と定義されており,java.io.PrintStream型である。System.inやSystem.errも同様の定義だ。

全体では2000行を超える大きなソースコードであるので,一部を抽出して引用する。

 このほかにSystemクラスの中ではSecurityManagerの設定や,システムプロパティを設定する処理が定義されている。更にJava 9から,ログの設定までjava.lang.Systemクラスの中にインナークラス等として導入されている。ログの扱いは一つのまとまった機能であるので,独立したクラスとした方がよいようにも思えるが,なぜこのような実装になっているのかは不明である。

 後半はシステムの初期化の処理が書かれている。コメントにはJava VMから呼び出されるという趣旨のメッセージが書かれている。Java VMはC言語で書かれているので,そこから直接呼ぶのであろう。

/*
 * Copyright (c) 1994, 2019, 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;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Console;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.module.ModuleDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.nio.charset.CharacterCodingException;
import java.security.AccessControlContext;
import java.security.ProtectionDomain;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.nio.channels.Channel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.PropertyPermission;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream;

import jdk.internal.util.StaticProperty;
import jdk.internal.module.ModuleBootstrap;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.HotSpotIntrinsicCandidate;
import jdk.internal.access.JavaLangAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.VM;
import jdk.internal.logger.LoggerFinderLoader;
import jdk.internal.logger.LazyLoggers;
import jdk.internal.logger.LocalizedLoggerWrapper;
import jdk.internal.util.SystemProps;
import jdk.internal.vm.annotation.Stable;
import sun.reflect.annotation.AnnotationType;
import sun.nio.ch.Interruptible;
import sun.security.util.SecurityConstants;

public final class System {
    public static final InputStream in = null;

    public static final PrintStream out = null;

    public static final PrintStream err = null;

    // indicates if a security manager is possible
    private static final int NEVER = 1;
    private static final int MAYBE = 2;
    private static @Stable int allowSecurityManager;

    // current security manager
    private static volatile SecurityManager security;   // read by VM

    // return true if a security manager is allowed
    private static boolean allowSecurityManager() {
        return (allowSecurityManager != NEVER);
    }

    public static void setIn(InputStream in) {
        checkIO();
        setIn0(in);
    }

    public static void setOut(PrintStream out) {
        checkIO();
        setOut0(out);
    }

    public static void setErr(PrintStream err) {
        checkIO();
        setErr0(err);
    }

    private static volatile Console cons;

     public static Console console() {
         Console c;
         if ((c = cons) == null) {
             synchronized (System.class) {
                 if ((c = cons) == null) {
                     cons = c = SharedSecrets.getJavaIOAccess().console();
                 }
             }
         }
         return c;
     }

    private static void checkIO() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission(“setIO”));
        }
    }

    public static void setSecurityManager(SecurityManager sm) {
        if (allowSecurityManager()) {
            if (security == null) {
                // ensure image reader is initialized
                Object.class.getResource(“java/lang/ANY”);
            }
            if (sm != null) {
                try {
                    // pre-populates the SecurityManager.packageAccess cache
                    // to avoid recursive permission checking issues with custom
                    // SecurityManager implementations
                    sm.checkPackageAccess(“java.lang”);
                } catch (Exception e) {
                    // no-op
                }
            }
            setSecurityManager0(sm);
        } else {
            // security manager not allowed
            if (sm != null) {
                throw new UnsupportedOperationException(
                    “Runtime configured to disallow security manager”);
            }
        }
    }

    private static synchronized
    void setSecurityManager0(final SecurityManager s) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission(“setSecurityManager”));
        }

        if ((s != null) && (s.getClass().getClassLoader() != null)) {
            AccessController.doPrivileged(new PrivilegedAction<>() {
                public Object run() {
                    s.getClass().getProtectionDomain().implies
                        (SecurityConstants.ALL_PERMISSION);
                    return null;
                }
            });
        }
        security = s;
    }
    public static SecurityManager getSecurityManager() {
        if (allowSecurityManager()) {
            return security;
        } else {
            return null;
        }
    }
    private static Properties props;

    public static Properties getProperties() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertiesAccess();
        }
        return props;
    }

    public static String lineSeparator() {
        return lineSeparator;
    }

    private static String lineSeparator;

    public static void setProperties(Properties props) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertiesAccess();
        }

        if (props == null) {
            Map<String, String> tempProps = SystemProps.initProperties();
            VersionProps.init(tempProps);
            props = createProperties(tempProps);
        }
        System.props = props;
    }

    public static String getProperty(String key) {
        checkKey(key);
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertyAccess(key);
        }

        return props.getProperty(key);
    }

    public static String getProperty(String key, String def) {
        checkKey(key);
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertyAccess(key);
        }

        return props.getProperty(key, def);
    }

    public static String setProperty(String key, String value) {
        checkKey(key);
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new PropertyPermission(key,
                SecurityConstants.PROPERTY_WRITE_ACTION));
        }

        return (String) props.setProperty(key, value);
    }

    public static String clearProperty(String key) {
        checkKey(key);
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new PropertyPermission(key, “write”));
        }

        return (String) props.remove(key);
    }

    private static void checkKey(String key) {
        if (key == null) {
            throw new NullPointerException(“key can’t be null”);
        }
        if (key.isEmpty()) {
            throw new IllegalArgumentException(“key can’t be empty”);
        }
    }

    public static String getenv(String name) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission(“getenv.”+name));
        }
        return ProcessEnvironment.getenv(name);
    }


    public static java.util.Map<String,String> getenv() {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission(“getenv.*”));
        }
        return ProcessEnvironment.getenv();
    }

    public interface Logger {
        // 省略
    }
    public static abstract class LoggerFinder {
        //省略
    }
    @CallerSensitive
    public static Logger getLogger(String name) {
        Objects.requireNonNull(name);
        final Class<?> caller = Reflection.getCallerClass();
        if (caller == null) {
            throw new IllegalCallerException(“no caller frame”);
        }
        return LazyLoggers.getLogger(name, caller.getModule());
    }
    @CallerSensitive
    public static Logger getLogger(String name, ResourceBundle bundle) {
        final ResourceBundle rb = Objects.requireNonNull(bundle);
        Objects.requireNonNull(name);
        final Class<?> caller = Reflection.getCallerClass();
        if (caller == null) {
            throw new IllegalCallerException(“no caller frame”);
        }
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            final PrivilegedAction<Logger> pa =
                    () -> LoggerFinder.accessProvider()
                            .getLocalizedLogger(name, rb, caller.getModule());
            return AccessController.doPrivileged(pa, null,
                                         LoggerFinder.LOGGERFINDER_PERMISSION);
        }
        return LoggerFinder.accessProvider()
                .getLocalizedLogger(name, rb, caller.getModule());
    }
    public static void exit(int status) {
        Runtime.getRuntime().exit(status);
    }
    public static void gc() {
        Runtime.getRuntime().gc();
    }
    public static void runFinalization() {
        Runtime.getRuntime().runFinalization();
    }
    @CallerSensitive
    public static void load(String filename) {
        Runtime.getRuntime().load0(Reflection.getCallerClass(), filename);
    }
    @CallerSensitive
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
    }
    public static native String mapLibraryName(String libname);
    private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
       if (enc != null) {
            try {
                return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
            } catch (UnsupportedEncodingException uee) {}
        }
        return new PrintStream(new BufferedOutputStream(fos, 128), true);
    }
    private static void logInitException(boolean printToStderr,
                                         boolean printStackTrace,
                                         String msg,
                                         Throwable e) {
        if (VM.initLevel() < 1) {
            throw new InternalError(“system classes not initialized”);
        }
        PrintStream log = (printToStderr) ? err : out;
        if (msg != null) {
            log.println(msg);
        }
        if (printStackTrace) {
            e.printStackTrace(log);
        } else {
            log.println(e);
            for (Throwable suppressed : e.getSuppressed()) {
                log.println(“Suppressed: ” + suppressed);
            }
            Throwable cause = e.getCause();
            if (cause != null) {
                log.println(“Caused by: ” + cause);
            }
        }
    }
    private static Properties createProperties(Map<String, String> initialProps) {
        Properties properties = new Properties(initialProps.size());
        for (var entry : initialProps.entrySet()) {
            String prop = entry.getKey();
            switch (prop) {
                // Do not add private system properties to the Properties
                case “sun.nio.MaxDirectMemorySize”:
                case “sun.nio.PageAlignDirectMemory”:
                    // used by java.lang.Integer.IntegerCache
                case “java.lang.Integer.IntegerCache.high”:
                    // used by sun.launcher.LauncherHelper
                case “sun.java.launcher.diag”:
                    // used by jdk.internal.loader.ClassLoaders
                case “jdk.boot.class.path.append”:
                    break;
                default:
                    properties.put(prop, entry.getValue());
            }
        }
        return properties;
    }
    private static void initPhase1() {
        // VM might invoke JNU_NewStringPlatform() to set those encoding
        // sensitive properties (user.home, user.name, boot.class.path, etc.)
        // during “props” initialization.
        // The charset is initialized in System.c and does not depend on the Properties.
        Map<String, String> tempProps = SystemProps.initProperties();
        VersionProps.init(tempProps);

        // There are certain system configurations that may be controlled by
        // VM options such as the maximum amount of direct memory and
        // Integer cache size used to support the object identity semantics
        // of autoboxing.  Typically, the library will obtain these values
        // from the properties set by the VM.  If the properties are for
        // internal implementation use only, these properties should be
        // masked from the system properties.
        //
        // Save a private copy of the system properties object that
        // can only be accessed by the internal implementation.
        VM.saveProperties(tempProps);
        props = createProperties(tempProps);

        StaticProperty.javaHome();          // Load StaticProperty to cache the property values

        lineSeparator = props.getProperty(“line.separator”);

        FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
        FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
        FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
        setIn0(new BufferedInputStream(fdIn));
        setOut0(newPrintStream(fdOut, props.getProperty(“sun.stdout.encoding”)));
        setErr0(newPrintStream(fdErr, props.getProperty(“sun.stderr.encoding”)));

        // Setup Java signal handlers for HUP, TERM, and INT (where available).
        Terminator.setup();

        // Initialize any miscellaneous operating system settings that need to be
        // set for the class libraries. Currently this is no-op everywhere except
        // for Windows where the process-wide error mode is set before the java.io
        // classes are used.
        VM.initializeOSEnvironment();

        // The main thread is not added to its thread group in the same
        // way as other threads; we must do it ourselves here.
        Thread current = Thread.currentThread();
        current.getThreadGroup().add(current);

        // register shared secrets
        setJavaLangAccess();

        // Subsystems that are invoked during initialization can invoke
        // VM.isBooted() in order to avoid doing things that should
        // wait until the VM is fully initialized. The initialization level
        // is incremented from 0 to 1 here to indicate the first phase of
        // initialization has completed.
        // IMPORTANT: Ensure that this remains the last initialization action!
        VM.initLevel(1);
    }

    // @see #initPhase2()
    static ModuleLayer bootLayer;

    /*
     * Invoked by VM.  Phase 2 module system initialization.
     * Only classes in java.base can be loaded in this phase.
     *
     * @param printToStderr print exceptions to stderr rather than stdout
     * @param printStackTrace print stack trace when exception occurs
     *
     * @return JNI_OK for success, JNI_ERR for failure
     */
    private static int initPhase2(boolean printToStderr, boolean printStackTrace) {
        try {
            bootLayer = ModuleBootstrap.boot();
        } catch (Exception | Error e) {
            logInitException(printToStderr, printStackTrace,
                             “Error occurred during initialization of boot layer”, e);
            return -1; // JNI_ERR
        }

        // module system initialized
        VM.initLevel(2);

        return 0; // JNI_OK
    }

    /*
     * Invoked by VM.  Phase 3 is the final system initialization:
     * 1. set security manager
     * 2. set system class loader
     * 3. set TCCL
     *
     * This method must be called after the module system initialization.
     * The security manager and system class loader may be a custom class from
     * the application classpath or modulepath.
     */
    private static void initPhase3() {
        String smProp = System.getProperty(“java.security.manager”);
        if (smProp != null) {
            switch (smProp) {
                case “disallow”:
                    allowSecurityManager = NEVER;
                    break;
                case “allow”:
                    allowSecurityManager = MAYBE;
                    break;
                case “”:
                case “default”:
                    setSecurityManager(new SecurityManager());
                    allowSecurityManager = MAYBE;
                    break;
                default:
                    try {
                        ClassLoader cl = ClassLoader.getBuiltinAppClassLoader();
                        Class<?> c = Class.forName(smProp, false, cl);
                        Constructor<?> ctor = c.getConstructor();
                        // Must be a public subclass of SecurityManager with
                        // a public no-arg constructor
                        if (!SecurityManager.class.isAssignableFrom(c) ||
                            !Modifier.isPublic(c.getModifiers()) ||
                            !Modifier.isPublic(ctor.getModifiers())) {
                            throw new Error(“Could not create SecurityManager: “
                                             + ctor.toString());
                        }
                        // custom security manager may be in non-exported package
                        ctor.setAccessible(true);
                        SecurityManager sm = (SecurityManager) ctor.newInstance();
                        setSecurityManager(sm);
                    } catch (Exception e) {
                        throw new InternalError(“Could not create SecurityManager”, e);
                    }
                    allowSecurityManager = MAYBE;
            }
        } else {
            allowSecurityManager = MAYBE;
        }

        // initializing the system class loader
        VM.initLevel(3);

        // system class loader initialized
        ClassLoader scl = ClassLoader.initSystemClassLoader();

        // set TCCL
        Thread.currentThread().setContextClassLoader(scl);

        // system is fully initialized
        VM.initLevel(4);
    }

    private static void setJavaLangAccess() {
        // Allow privileged classes outside of java.lang
        SharedSecrets.setJavaLangAccess(new JavaLangAccess() {
            public List<Method> getDeclaredPublicMethods(Class<?> klass, String name, Class<?>… parameterTypes) {
                return klass.getDeclaredPublicMethods(name, parameterTypes);
            }
            public jdk.internal.reflect.ConstantPool getConstantPool(Class<?> klass) {
                return klass.getConstantPool();
            }
            public boolean casAnnotationType(Class<?> klass, AnnotationType oldType, AnnotationType newType) {
                return klass.casAnnotationType(oldType, newType);
            }
            public AnnotationType getAnnotationType(Class<?> klass) {
                return klass.getAnnotationType();
            }
            public Map<Class<? extends Annotation>, Annotation> getDeclaredAnnotationMap(Class<?> klass) {
                return klass.getDeclaredAnnotationMap();
            }
            public byte[] getRawClassAnnotations(Class<?> klass) {
                return klass.getRawAnnotations();
            }
            public byte[] getRawClassTypeAnnotations(Class<?> klass) {
                return klass.getRawTypeAnnotations();
            }
            public byte[] getRawExecutableTypeAnnotations(Executable executable) {
                return Class.getExecutableTypeAnnotationBytes(executable);
            }
            public <E extends Enum<E>>
            E[] getEnumConstantsShared(Class<E> klass) {
                return klass.getEnumConstantsShared();
            }
            public void blockedOn(Interruptible b) {
                Thread.blockedOn(b);
            }
            public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
                Shutdown.add(slot, registerShutdownInProgress, hook);
            }
            public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) {
                return new Thread(target, acc);
            }
            @SuppressWarnings(“deprecation”)
            public void invokeFinalize(Object o) throws Throwable {
                o.finalize();
            }
            public ConcurrentHashMap<?, ?> createOrGetClassLoaderValueMap(ClassLoader cl) {
                return cl.createOrGetClassLoaderValueMap();
            }
            public Class<?> defineClass(ClassLoader loader, String name, byte[] b, ProtectionDomain pd, String source) {
                return ClassLoader.defineClass1(loader, name, b, 0, b.length, pd, source);
            }
            public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
                return cl.findBootstrapClassOrNull(name);
            }
            public Package definePackage(ClassLoader cl, String name, Module module) {
                return cl.definePackage(name, module);
            }
            public String fastUUID(long lsb, long msb) {
                return Long.fastUUID(lsb, msb);
            }
            public void addNonExportedPackages(ModuleLayer layer) {
                SecurityManager.addNonExportedPackages(layer);
            }
            public void invalidatePackageAccessCache() {
                SecurityManager.invalidatePackageAccessCache();
            }
            public Module defineModule(ClassLoader loader,
                                       ModuleDescriptor descriptor,
                                       URI uri) {
                return new Module(null, loader, descriptor, uri);
            }
            public Module defineUnnamedModule(ClassLoader loader) {
                return new Module(loader);
            }
            public void addReads(Module m1, Module m2) {
                m1.implAddReads(m2);
            }
            public void addReadsAllUnnamed(Module m) {
                m.implAddReadsAllUnnamed();
            }
            public void addExports(Module m, String pn, Module other) {
                m.implAddExports(pn, other);
            }
            public void addExportsToAllUnnamed(Module m, String pn) {
                m.implAddExportsToAllUnnamed(pn);
            }
            public void addOpens(Module m, String pn, Module other) {
                m.implAddOpens(pn, other);
            }
            public void addOpensToAllUnnamed(Module m, String pn) {
                m.implAddOpensToAllUnnamed(pn);
            }
            public void addOpensToAllUnnamed(Module m, Iterator<String> packages) {
                m.implAddOpensToAllUnnamed(packages);
            }
            public void addUses(Module m, Class<?> service) {
                m.implAddUses(service);
            }
            public boolean isReflectivelyExported(Module m, String pn, Module other) {
                return m.isReflectivelyExported(pn, other);
            }
            public boolean isReflectivelyOpened(Module m, String pn, Module other) {
                return m.isReflectivelyOpened(pn, other);
            }
            public ServicesCatalog getServicesCatalog(ModuleLayer layer) {
                return layer.getServicesCatalog();
            }
            public Stream<ModuleLayer> layers(ModuleLayer layer) {
                return layer.layers();
            }
            public Stream<ModuleLayer> layers(ClassLoader loader) {
                return ModuleLayer.layers(loader);
            }

            public String newStringNoRepl(byte[] bytes, Charset cs) throws CharacterCodingException  {
                return StringCoding.newStringNoRepl(bytes, cs);
            }

            public byte[] getBytesNoRepl(String s, Charset cs) throws CharacterCodingException {
                return StringCoding.getBytesNoRepl(s, cs);
            }

            public String newStringUTF8NoRepl(byte[] bytes, int off, int len) {
                return StringCoding.newStringUTF8NoRepl(bytes, off, len);
            }

            public byte[] getBytesUTF8NoRepl(String s) {
                return StringCoding.getBytesUTF8NoRepl(s);
            }

            public void setCause(Throwable t, Throwable cause) {
                t.setCause(cause);
            }
        });
    }
}